changeset 98:95307ad235d9

Added Draw2d code, still work in progress
author Frank Benoit <benoit@tionex.de>
date Sun, 03 Aug 2008 00:52:14 +0200
parents b492ba44e44d
children cdf77a0793c2
files dsss.conf dwtx/draw2d/AbsoluteBendpoint.d dwtx/draw2d/AbstractBackground.d dwtx/draw2d/AbstractBorder.d dwtx/draw2d/AbstractConnectionAnchor.d dwtx/draw2d/AbstractHintLayout.d dwtx/draw2d/AbstractLabeledBorder.d dwtx/draw2d/AbstractLayout.d dwtx/draw2d/AbstractLocator.d dwtx/draw2d/AbstractRouter.d dwtx/draw2d/AccessibleBase.d dwtx/draw2d/ActionEvent.d dwtx/draw2d/ActionListener.d dwtx/draw2d/AncestorHelper.d dwtx/draw2d/AncestorListener.d dwtx/draw2d/AnchorListener.d dwtx/draw2d/Animation.d dwtx/draw2d/Animator.d dwtx/draw2d/ArrowButton.d dwtx/draw2d/ArrowLocator.d dwtx/draw2d/AutomaticRouter.d dwtx/draw2d/Bendpoint.d dwtx/draw2d/BendpointConnectionRouter.d dwtx/draw2d/BendpointLocator.d dwtx/draw2d/Border.d dwtx/draw2d/BorderLayout.d dwtx/draw2d/BufferedGraphicsSource.d dwtx/draw2d/Button.d dwtx/draw2d/ButtonBorder.d dwtx/draw2d/ButtonGroup.d dwtx/draw2d/ButtonModel.d dwtx/draw2d/ButtonStateTransitionListener.d dwtx/draw2d/ChangeEvent.d dwtx/draw2d/ChangeListener.d dwtx/draw2d/CheckBox.d dwtx/draw2d/ChopboxAnchor.d dwtx/draw2d/Clickable.d dwtx/draw2d/ClickableEventHandler.d dwtx/draw2d/ColorConstants.d dwtx/draw2d/CompoundBorder.d dwtx/draw2d/Connection.d dwtx/draw2d/ConnectionAnchor.d dwtx/draw2d/ConnectionAnchorBase.d dwtx/draw2d/ConnectionEndpointLocator.d dwtx/draw2d/ConnectionLayer.d dwtx/draw2d/ConnectionLocator.d dwtx/draw2d/ConnectionRouter.d dwtx/draw2d/CoordinateListener.d dwtx/draw2d/Cursors.d dwtx/draw2d/DefaultRangeModel.d dwtx/draw2d/DeferredUpdateManager.d dwtx/draw2d/DelegatingLayout.d dwtx/draw2d/Ellipse.d dwtx/draw2d/EllipseAnchor.d dwtx/draw2d/EventDispatcher.d dwtx/draw2d/EventListenerList.d dwtx/draw2d/ExclusionSearch.d dwtx/draw2d/FanRouter.d dwtx/draw2d/Figure.d dwtx/draw2d/FigureCanvas.d dwtx/draw2d/FigureListener.d dwtx/draw2d/FigureUtilities.d dwtx/draw2d/FlowLayout.d dwtx/draw2d/FocusBorder.d dwtx/draw2d/FocusEvent.d dwtx/draw2d/FocusListener.d dwtx/draw2d/FocusTraverseManager.d dwtx/draw2d/FrameBorder.d dwtx/draw2d/FreeformFigure.d dwtx/draw2d/FreeformHelper.d dwtx/draw2d/FreeformLayer.d dwtx/draw2d/FreeformLayeredPane.d dwtx/draw2d/FreeformLayout.d dwtx/draw2d/FreeformListener.d dwtx/draw2d/FreeformViewport.d dwtx/draw2d/Graphics.d dwtx/draw2d/GraphicsSource.d dwtx/draw2d/GridData.d dwtx/draw2d/GridLayout.d dwtx/draw2d/GroupBoxBorder.d dwtx/draw2d/IFigure.d dwtx/draw2d/ImageFigure.d dwtx/draw2d/ImageUtilities.d dwtx/draw2d/InputEvent.d dwtx/draw2d/KeyEvent.d dwtx/draw2d/KeyListener.d dwtx/draw2d/Label.d dwtx/draw2d/LabelAnchor.d dwtx/draw2d/LabeledBorder.d dwtx/draw2d/LabeledContainer.d dwtx/draw2d/Layer.d dwtx/draw2d/LayeredPane.d dwtx/draw2d/LayoutAnimator.d dwtx/draw2d/LayoutListener.d dwtx/draw2d/LayoutManager.d dwtx/draw2d/LightweightSystem.d dwtx/draw2d/LineBorder.d dwtx/draw2d/Locator.d dwtx/draw2d/ManhattanConnectionRouter.d dwtx/draw2d/MarginBorder.d dwtx/draw2d/MidpointLocator.d dwtx/draw2d/MouseEvent.d dwtx/draw2d/MouseListener.d dwtx/draw2d/MouseMotionListener.d dwtx/draw2d/NativeGraphicsSource.d dwtx/draw2d/Orientable.d dwtx/draw2d/Panel.d dwtx/draw2d/Polygon.d dwtx/draw2d/PolygonDecoration.d dwtx/draw2d/Polyline.d dwtx/draw2d/PolylineConnection.d dwtx/draw2d/PolylineDecoration.d dwtx/draw2d/PopUpHelper.d dwtx/draw2d/PositionConstants.d dwtx/draw2d/PrintFigureOperation.d dwtx/draw2d/PrintOperation.d dwtx/draw2d/PrinterGraphics.d dwtx/draw2d/RangeModel.d dwtx/draw2d/RectangleFigure.d dwtx/draw2d/RelativeBendpoint.d dwtx/draw2d/RelativeLocator.d dwtx/draw2d/RotatableDecoration.d dwtx/draw2d/RoundedRectangle.d dwtx/draw2d/RoutingAnimator.d dwtx/draw2d/RoutingListener.d dwtx/draw2d/SWTEventDispatcher.d dwtx/draw2d/SWTGraphics.d dwtx/draw2d/ScalableFigure.d dwtx/draw2d/ScalableFreeformLayeredPane.d dwtx/draw2d/ScalableLayeredPane.d dwtx/draw2d/ScaledGraphics.d dwtx/draw2d/SchemeBorder.d dwtx/draw2d/ScrollBar.d dwtx/draw2d/ScrollBarLayout.d dwtx/draw2d/ScrollPane.d dwtx/draw2d/ScrollPaneLayout.d dwtx/draw2d/ScrollPaneSolver.d dwtx/draw2d/Shape.d dwtx/draw2d/ShortestPathConnectionRouter.d dwtx/draw2d/SimpleEtchedBorder.d dwtx/draw2d/SimpleLoweredBorder.d dwtx/draw2d/SimpleRaisedBorder.d dwtx/draw2d/StackLayout.d dwtx/draw2d/SubordinateUpdateManager.d dwtx/draw2d/TextUtilities.d dwtx/draw2d/TitleBarBorder.d dwtx/draw2d/Toggle.d dwtx/draw2d/ToggleButton.d dwtx/draw2d/ToggleModel.d dwtx/draw2d/ToolTipHelper.d dwtx/draw2d/ToolbarLayout.d dwtx/draw2d/TreeSearch.d dwtx/draw2d/Triangle.d dwtx/draw2d/UpdateListener.d dwtx/draw2d/UpdateManager.d dwtx/draw2d/Viewport.d dwtx/draw2d/ViewportLayout.d dwtx/draw2d/XYAnchor.d dwtx/draw2d/XYLayout.d dwtx/draw2d/geometry/Dimension.d dwtx/draw2d/geometry/Geometry.d dwtx/draw2d/geometry/Insets.d dwtx/draw2d/geometry/Point.d dwtx/draw2d/geometry/PointList.d dwtx/draw2d/geometry/PrecisionDimension.d dwtx/draw2d/geometry/PrecisionPoint.d dwtx/draw2d/geometry/PrecisionRectangle.d dwtx/draw2d/geometry/Ray.d dwtx/draw2d/geometry/Rectangle.d dwtx/draw2d/geometry/Transform.d dwtx/draw2d/geometry/Translatable.d dwtx/draw2d/geometry/Transposer.d dwtx/draw2d/graph/BreakCycles.d dwtx/draw2d/graph/Cell.d dwtx/draw2d/graph/CollapsedEdges.d dwtx/draw2d/graph/CompoundBreakCycles.d dwtx/draw2d/graph/CompoundDirectedGraph.d dwtx/draw2d/graph/CompoundDirectedGraphLayout.d dwtx/draw2d/graph/CompoundHorizontalPlacement.d dwtx/draw2d/graph/CompoundPopulateRanks.d dwtx/draw2d/graph/CompoundRankSorter.d dwtx/draw2d/graph/CompoundVerticalPlacement.d dwtx/draw2d/graph/ConvertCompoundGraph.d dwtx/draw2d/graph/DirectedGraph.d dwtx/draw2d/graph/DirectedGraphLayout.d dwtx/draw2d/graph/Edge.d dwtx/draw2d/graph/EdgeList.d dwtx/draw2d/graph/GraphUtilities.d dwtx/draw2d/graph/GraphVisitor.d dwtx/draw2d/graph/HorizontalPlacement.d dwtx/draw2d/graph/InitialRankSolver.d dwtx/draw2d/graph/InvertEdges.d dwtx/draw2d/graph/LocalOptimizer.d dwtx/draw2d/graph/MinCross.d dwtx/draw2d/graph/NestingTree.d dwtx/draw2d/graph/Node.d dwtx/draw2d/graph/NodeCluster.d dwtx/draw2d/graph/NodeList.d dwtx/draw2d/graph/NodePair.d dwtx/draw2d/graph/Obstacle.d dwtx/draw2d/graph/Path.d dwtx/draw2d/graph/PopulateRanks.d dwtx/draw2d/graph/Rank.d dwtx/draw2d/graph/RankAssignmentSolver.d dwtx/draw2d/graph/RankList.d dwtx/draw2d/graph/RankSorter.d dwtx/draw2d/graph/RevertableChange.d dwtx/draw2d/graph/RouteEdges.d dwtx/draw2d/graph/Segment.d dwtx/draw2d/graph/ShortestPathRouter.d dwtx/draw2d/graph/SortSubgraphs.d dwtx/draw2d/graph/SpanningTreeVisitor.d dwtx/draw2d/graph/Subgraph.d dwtx/draw2d/graph/SubgraphBoundary.d dwtx/draw2d/graph/TightSpanningTreeSolver.d dwtx/draw2d/graph/TransposeMetrics.d dwtx/draw2d/graph/Vertex.d dwtx/draw2d/graph/VerticalPlacement.d dwtx/draw2d/graph/VirtualNode.d dwtx/draw2d/graph/VirtualNodeCreation.d dwtx/draw2d/internal/MultiValueMap.d dwtx/draw2d/parts/ScrollableThumbnail.d dwtx/draw2d/parts/Thumbnail.d dwtx/draw2d/text/AbstractFlowBorder.d dwtx/draw2d/text/BidiChars.d dwtx/draw2d/text/BidiInfo.d dwtx/draw2d/text/BidiProcessor.d dwtx/draw2d/text/BlockBox.d dwtx/draw2d/text/BlockFlow.d dwtx/draw2d/text/BlockFlowLayout.d dwtx/draw2d/text/CaretInfo.d dwtx/draw2d/text/CompositeBox.d dwtx/draw2d/text/ContentBox.d dwtx/draw2d/text/FlowAdapter.d dwtx/draw2d/text/FlowBorder.d dwtx/draw2d/text/FlowBox.d dwtx/draw2d/text/FlowContainerLayout.d dwtx/draw2d/text/FlowContext.d dwtx/draw2d/text/FlowFigure.d dwtx/draw2d/text/FlowFigureLayout.d dwtx/draw2d/text/FlowPage.d dwtx/draw2d/text/FlowUtilities.d dwtx/draw2d/text/InlineFlow.d dwtx/draw2d/text/InlineFlowLayout.d dwtx/draw2d/text/LineBox.d dwtx/draw2d/text/LineRoot.d dwtx/draw2d/text/NestedLine.d dwtx/draw2d/text/PageFlowLayout.d dwtx/draw2d/text/ParagraphTextLayout.d dwtx/draw2d/text/SimpleTextLayout.d dwtx/draw2d/text/TextFlow.d dwtx/draw2d/text/TextFragmentBox.d dwtx/draw2d/text/TextLayout.d dwtx/draw2d/widgets/ImageBorder.d dwtx/draw2d/widgets/MultiLineLabel.d dwtx/dwtxhelper/mangoicu/UBreakIterator.d
diffstat 256 files changed, 48415 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/dsss.conf	Sat Jul 26 14:39:08 2008 +0200
+++ b/dsss.conf	Sun Aug 03 00:52:14 2008 +0200
@@ -1,9 +1,13 @@
 
 
 
+[dwtx/draw2d]
+type=library
+buildflags+=-Jres
 
 [dwtx]
 type=library
+exclude=dwtx/draw2d
 buildflags+=-Jres
 preinstall = \
     installdir res $LIB_PREFIX/res
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/AbsoluteBendpoint.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.AbsoluteBendpoint;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.Bendpoint;
+
+/**
+ * AbsoluteBendpoint is a Bendpoint that defines its location simply as its X and Y
+ * coordinates. It is used by bendable {@link Connection Connections}.
+ */
+public class AbsoluteBendpoint
+    : Point
+    , Bendpoint
+{
+
+/**
+ * Creates a new AbsoluteBendpoint at the Point p.
+ * @param p The absolute location of the bendpoint
+ * @since 2.0
+ */
+public this(Point p) {
+    super(p);
+}
+
+/**
+ * Creates a new AbsoluteBendpoint at the Point (x,y).
+ * @param x The X coordinate
+ * @param y The Y coordinate
+ * @since 2.0
+ */
+public this(int x, int y) {
+    super(x, y);
+}
+
+/**
+ * @see dwtx.draw2d.Bendpoint#getLocation()
+ */
+public Point getLocation() {
+    return this;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/AbstractBackground.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.AbstractBackground;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.AbstractBorder;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Graphics;
+
+/**
+ * A special border which can paint both underneath and on top of a Figure.
+ * Normal borders only paint on top of a figure and its children. A background
+ * has the opportunity to paint both first, and optionally last.
+ * <P>
+ * WARNING: Experimental for 3.3. Clients should help validate the use cases
+ * of this new function.
+ * @since 3.3
+ */
+public class AbstractBackground : AbstractBorder {
+
+    /**
+     * {@inheritDoc}
+     */
+    public Insets getInsets(IFigure figure) {
+        return IFigure_NO_INSETS;
+    }
+
+    /**
+     * {@inheritDoc}
+     * By default, this method is stubbed out for backgrounds which only paint
+     * underneath a figure.
+     */
+    public void paint(IFigure figure, Graphics graphics, Insets insets) {
+    }
+
+    /**
+     * Called when this Background should paint. If the background is being painted
+     * inside another border or background, the insets indicate how far inside the
+     * target figure the background should be painted. In most cases, the insets
+     * will be all zero.
+     * @param figure The figure on which the background is being painted
+     * @param graphics The graphics
+     * @param insets Amount to inset from the figure's bounds
+     * @since 3.2
+     */
+    public void paintBackground(IFigure figure, Graphics graphics, Insets insets) {
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/AbstractBorder.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.AbstractBorder;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.Border;
+import dwtx.draw2d.IFigure;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Rectangle;
+
+/**
+ * Provides generic support for borders.
+ * @author hudsonr
+ */
+public abstract class AbstractBorder
+    : Border
+{
+
+private static const Dimension EMPTY;
+
+/** A temporary Rectangle*/
+protected static Rectangle tempRect;
+
+static this(){
+    EMPTY = new Dimension();
+    tempRect = new Rectangle();
+}
+
+/**
+ * Returns a temporary rectangle representing the figure's bounds cropped by the specified
+ * insets.  This method exists for convenience and performance; the method does not new
+ * any Objects and returns a rectangle which the caller can manipulate.
+ * @since 2.0
+ * @param figure  Figure for which the paintable rectangle is needed
+ * @param insets The insets
+ * @return The paintable region on the Figure f
+ */
+protected static final Rectangle getPaintRectangle(IFigure figure, Insets insets) {
+    tempRect.setBounds(figure.getBounds());
+    return tempRect.crop(insets);
+}
+
+/**
+ * @see dwtx.draw2d.Border#getPreferredSize(IFigure)
+ */
+public Dimension getPreferredSize(IFigure f) {
+    return EMPTY;
+}
+
+/**
+ * @see dwtx.draw2d.Border#isOpaque()
+ */
+public bool isOpaque() {
+    return false;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/AbstractConnectionAnchor.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.AbstractConnectionAnchor;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.ConnectionAnchorBase;
+import dwtx.draw2d.AncestorListener;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.AnchorListener;
+
+/**
+ * Provides support for anchors which depend on a figure for thier location.
+ * @author hudsonr
+ */
+public abstract class AbstractConnectionAnchor
+    : ConnectionAnchorBase
+    , AncestorListener
+{
+
+private IFigure owner;
+
+/**
+ * Constructs an AbstractConnectionAnchor with no owner.
+ *
+ * @since 2.0
+ */
+public this() { }
+
+/**
+ * Constructs an AbstractConnectionAnchor with the owner supplied as input.
+ *
+ * @since 2.0
+ * @param owner  Owner of this anchor
+ */
+public this(IFigure owner) {
+    setOwner(owner);
+}
+
+/**
+ * Adds the given listener to the listeners to be notified of anchor location changes.
+ *
+ * @since 2.0
+ * @param listener   Listener to be added
+ * @see  #removeAnchorListener(AnchorListener)
+ */
+public void addAnchorListener(AnchorListener listener) {
+    if (listener is null)
+        return;
+    if (listeners.size() is 0)
+        getOwner().addAncestorListener(this);
+    super.addAnchorListener(listener);
+}
+
+/**
+ * Notifies all the listeners of this anchor's location change.
+ *
+ * @since 2.0
+ * @param figure  Anchor-owning Figure which has moved
+ * @see dwtx.draw2d.AncestorListener#ancestorMoved(IFigure)
+ */
+public void ancestorMoved(IFigure figure) {
+    fireAnchorMoved();
+}
+
+/**
+ * @see dwtx.draw2d.AncestorListener#ancestorAdded(IFigure)
+ */
+public void ancestorAdded(IFigure ancestor) { }
+
+/**
+ * @see dwtx.draw2d.AncestorListener#ancestorRemoved(IFigure)
+ */
+public void ancestorRemoved(IFigure ancestor) { }
+
+/**
+ * Returns the owner Figure on which this anchor's location is dependent.
+ *
+ * @since 2.0
+ * @return  Owner of this anchor
+ * @see #setOwner(IFigure)
+ */
+public IFigure getOwner() {
+    return owner;
+}
+
+/**
+ * Returns the point which is used as the reference by this AbstractConnectionAnchor. It
+ * is generally dependent on the Figure which is the owner of this
+ * AbstractConnectionAnchor.
+ *
+ * @since 2.0
+ * @return  The reference point of this anchor
+ * @see dwtx.draw2d.ConnectionAnchor#getReferencePoint()
+ */
+public Point getReferencePoint() {
+    if (getOwner() is null)
+        return null;
+    else {
+        Point ref_ = getOwner().getBounds().getCenter();
+        getOwner().translateToAbsolute(ref_);
+        return ref_;
+    }
+}
+
+/**
+ * Removes the given listener from this anchor. If all the listeners are removed, then
+ * this anchor removes itself from its owner.
+ *
+ * @since 2.0
+ * @param listener  Listener to be removed from this anchors listeners list
+ * @see #addAnchorListener(AnchorListener)
+ */
+public void removeAnchorListener(AnchorListener listener) {
+    super.removeAnchorListener(listener);
+    if (listeners.size() is 0)
+        getOwner().removeAncestorListener(this);
+}
+
+/**
+ * Sets the owner of this anchor, on whom this anchors location is dependent.
+ *
+ * @since 2.0
+ * @param owner  Owner of this anchor
+ */
+public void setOwner(IFigure owner) {
+    this.owner = owner;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/AbstractHintLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.AbstractHintLayout;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.AbstractLayout;
+import dwtx.draw2d.IFigure;
+
+
+/**
+ * The foundation for layout managers which are sensitive to width and/or height hints.
+ * This class will cache preferred and minimum sizes for a given set of hints. If the
+ * hints change in a meaningful way, the cached size is thrown out and redetermined.
+ * <P>
+ * Subclasses may be sensitive to one or both hints. By default, this class assumes both
+ * hints are important. Subclasses may override this behavior in {@link
+ * #isSensitiveHorizontally(IFigure)} and {@link #isSensitiveVertically(IFigure)}. At
+ * least one of these method should return <code>true</code>.
+ *
+ * @author hudsonr
+ * @since 2.0
+ */
+public abstract class AbstractHintLayout
+    : AbstractLayout
+{
+
+private Dimension minimumSize = null;
+private Dimension cachedPreferredHint;
+private Dimension cachedMinimumHint;
+
+this(){
+    cachedPreferredHint = new Dimension(-1, -1);
+    cachedMinimumHint = new Dimension(-1, -1);
+}
+/**
+ * Calculates the minimum size using the given width and height hints. This method is
+ * called from {@link #getMinimumSize(IFigure, int, int)} whenever the cached minimum size
+ * has been flushed.
+ * <P>
+ * By default, this method just calls {@link #getPreferredSize(IFigure, int, int)},
+ * meaning minimum and preferres sizes will be the same unless this method is overridden.
+ *
+ * @param container the Figure on which this layout is installed
+ * @param wHint the width hint
+ * @param hHint the height hint
+ *
+ * @return the layout's minimum size
+ */
+protected Dimension calculateMinimumSize(IFigure container, int wHint, int hHint) {
+    return getPreferredSize(container, wHint, hHint);
+}
+
+/**
+ * @see dwtx.draw2d.LayoutManager#getMinimumSize(IFigure, int, int)
+ */
+public Dimension getMinimumSize(IFigure container, int w, int h) {
+    bool flush = cachedMinimumHint.width !is w
+        && isSensitiveHorizontally(container);
+    flush |= cachedMinimumHint.height !is h
+        && isSensitiveVertically(container);
+    if (flush) {
+        minimumSize = null;
+        cachedMinimumHint.width = w;
+        cachedMinimumHint.height = h;
+    }
+    if (minimumSize is null)
+        minimumSize = calculateMinimumSize(container, w, h);
+    return minimumSize;
+}
+
+/**
+ * @see dwtx.draw2d.LayoutManager#getPreferredSize(IFigure, int, int)
+ */
+public final Dimension getPreferredSize(IFigure container, int w, int h) {
+    bool flush = cachedPreferredHint.width !is w
+        && isSensitiveHorizontally(container);
+    flush |= cachedPreferredHint.height !is h
+        && isSensitiveVertically(container);
+    if (flush) {
+        preferredSize = null;
+        cachedPreferredHint.width = w;
+        cachedPreferredHint.height = h;
+    }
+    return super.getPreferredSize(container, w, h);
+}
+
+/**
+ * Extends the superclass implementation to flush the cached minimum size.
+ * @see dwtx.draw2d.LayoutManager#invalidate()
+ */
+public void invalidate() {
+    minimumSize = null;
+    super.invalidate();
+}
+
+/**
+ * Returns whether this layout manager is sensitive to changes in the horizontal hint. By
+ * default, this method returns <code>true</code>.
+ * @param container the layout's container
+ * @return <code>true</code> if this layout is sensite to horizontal hint changes
+ */
+protected bool isSensitiveHorizontally(IFigure container) {
+    return true;
+}
+
+/**
+ * Returns whether this layout manager is sensitive to changes in the vertical hint. By
+ * default, this method returns <code>true</code>.
+ * @param container the layout's container
+ * @return <code>true</code> if this layout is sensite to vertical hint changes
+ */
+protected bool isSensitiveVertically(IFigure container) {
+    return true;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/AbstractLabeledBorder.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,173 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.AbstractLabeledBorder;
+
+import dwt.dwthelper.utils;
+
+
+
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.AbstractBorder;
+import dwtx.draw2d.LabeledBorder;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.ColorConstants;
+import dwtx.draw2d.FigureUtilities;
+
+/**
+ * Provides support for a border with a label describing the contents of which it is
+ * surrounding.
+ */
+public abstract class AbstractLabeledBorder
+    : AbstractBorder
+    , LabeledBorder
+{
+
+private Dimension textExtents;
+private String label;
+private Insets insets;
+private Color textColor;
+private Font font;
+
+/**
+ * Constructs a default AbstractLabeledBorder with the name of this class set as its
+ * label.
+ *
+ * @since 2.0
+ */
+public this() {
+    textColor = ColorConstants.black;
+    String className = this.classinfo.name;
+    setLabel(className.substring(className.lastIndexOf('.') + 1, className.length));
+}
+
+/**
+ * Constructs a border with the label set to the String passed in as input.
+ *
+ * @param s  Label to be set on the border
+ * @since 2.0
+ */
+public this(String s) {
+    textColor = ColorConstants.black;
+    setLabel(s);
+}
+
+/**
+ * Calculates insets based on the current font and other attributes. This value will be
+ * cached until {@link #invalidate()} is called.
+ * @param figure The figure to which the border is being applied
+ * @return The Insets
+ */
+protected abstract Insets calculateInsets(IFigure figure);
+
+/**
+ * Returns the font that this border will use. If no Font has been specified, the font
+ * associated with the input Figure will be used.
+ * @param f Figure used to get a default font
+ * @return The font for this border
+ */
+protected Font getFont(IFigure f) {
+    if (font is null)
+        return f.getFont();
+    return font;
+}
+
+/**
+ * Returns the insets, or space associated for this border. Returns any previously set
+ * value if present, else calculates it from the Figure provided in as input.
+ * @param fig Figure used to calculate insets
+ * @return The insets
+ */
+public Insets getInsets(IFigure fig) {
+    if (insets is null)
+        insets = calculateInsets(fig);
+    return insets;
+}
+
+/**
+ * @see dwtx.draw2d.LabeledBorder#getLabel()
+ */
+public String getLabel() {
+    return label;
+}
+
+/**
+ * @see dwtx.draw2d.Border#getPreferredSize(IFigure)
+ */
+public Dimension getPreferredSize(IFigure fig) {
+    return new Dimension(getTextExtents(fig));
+}
+
+/**
+ * Returns the text Color of this AbstractLabeledBorder's label.
+ *
+ * @return The text color
+ * @since 2.0
+ */
+public Color getTextColor() {
+    return textColor;
+}
+
+/**
+ * Calculates and returns the size required by this border's label.
+ *
+ * @param f  IFigure on which the calculations are to be made
+ * @return   Dimensions required by the text of this border's label
+ * @since 2.0
+ */
+protected Dimension getTextExtents(IFigure f) {
+    if (textExtents is null)
+        textExtents = FigureUtilities.getTextExtents(label, getFont(f));
+    return textExtents;
+}
+
+/**
+ * Resets the internal values and state so that they can be recalculated. Called whenever
+ * a state  change has occurred that effects the insets or  text extents of this border.
+ */
+protected void invalidate() {
+    insets = null;
+    textExtents = null;
+}
+
+/**
+ * Sets the Font of this border to the input value, and invalidates the border forcing an
+ * update of internal parameters of insets and text extents.
+ * @param font The font
+ */
+public void setFont(Font font) {
+    this.font = font;
+    invalidate();
+}
+
+/**
+ * @see dwtx.draw2d.LabeledBorder#setLabel(String)
+ */
+public void setLabel(String s) {
+    label = ((s is null) ? "" : s);  //$NON-NLS-1$
+    invalidate();
+}
+
+/**
+ * Sets the color for this border's text.
+ *
+ * @param color  Color to be set for this border's text
+ * @since 2.0
+ */
+public void setTextColor(Color color) {
+    textColor = color;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/AbstractLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,161 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.AbstractLayout;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.LayoutManager;
+
+/**
+ * Provides generic support for LayoutManagers.
+ */
+public abstract class AbstractLayout
+    : LayoutManager
+{
+
+/**
+ * The cached preferred size.
+ */
+protected Dimension preferredSize;
+
+/**
+ * Whether or not this layout pays attention to visiblity of figures when
+ * calculating its bounds. By default, false.
+ */
+protected bool isObservingVisibility_ = false;
+
+/**
+ * This method is now {@link #calculatePreferredSize(IFigure, int, int)}.
+ * @param container the figure
+ */
+protected final void calculatePreferredSize(IFigure container) { }
+
+/**
+ * Calculates the preferred size of the given figure, using width and height hints.
+ * @param container The figure
+ * @param wHint The width hint
+ * @param hHint The height hint
+ * @return The preferred size
+ */
+protected abstract Dimension calculatePreferredSize(IFigure container,
+    int wHint, int hHint);
+
+/**
+ * Returns the preferred size of the figure's border.
+ * @param container The figure that the border is on
+ * @return The border's preferred size
+ */
+protected Dimension getBorderPreferredSize(IFigure container) {
+    if (container.getBorder() is null)
+        return new Dimension();
+    return container.getBorder().getPreferredSize(container);
+}
+
+/**
+ * Returns the constraint for the given figure.
+ * @param child The figure
+ * @return The constraint
+ */
+public Object getConstraint(IFigure child) {
+    return null;
+}
+
+/**
+ * This method is now {@link #getMinimumSize(IFigure, int, int)}.
+ * @param container the figure
+ */
+public final void getMinimumSize(IFigure container) { }
+
+/**
+ * @see dwtx.draw2d.LayoutManager#getMinimumSize(IFigure, int, int)
+ */
+public Dimension getMinimumSize(IFigure container, int wHint, int hHint) {
+    return getPreferredSize(container, wHint, hHint);
+}
+
+/**
+ * Returns the preferred size of the given figure, using width and height hints.  If the
+ * preferred size is cached, that size  is returned.  Otherwise, {@link
+ * #calculatePreferredSize(IFigure, int, int)} is called.
+ * @param container The figure
+ * @param wHint The width hint
+ * @param hHint The height hint
+ * @return The preferred size
+ */
+public Dimension getPreferredSize(IFigure container, int wHint, int hHint) {
+    if (preferredSize is null)
+        preferredSize = calculatePreferredSize(container, wHint, hHint);
+    return preferredSize;
+}
+
+/**
+ * This method is now {@link #getPreferredSize(IFigure, int, int)}.
+ * @param container the figure
+ */
+public final void getPreferredSize(IFigure container) { }
+
+
+/**
+ * @see dwtx.draw2d.LayoutManager#invalidate()
+ */
+public void invalidate() {
+    preferredSize = null;
+}
+
+/**
+ * Removes any cached information about the given figure.
+ * @param child the child that is invalidated
+ */
+protected void invalidate(IFigure child) {
+    invalidate();
+}
+
+/**
+ * Returns whether or not this layout pays attention to visiblity when calculating its
+ * bounds.
+ * @return true if invisible figures should not contribute to this layout's bounds.
+ */
+public bool isObservingVisibility() {
+    return isObservingVisibility_;
+}
+
+/**
+ * Removes the given figure from this LayoutManager's list of figures.
+ * @param child The figure to remove
+ */
+public void remove(IFigure child) {
+    invalidate();
+}
+
+/**
+ * Sets the constraint for the given figure.
+ * @param child the child
+ * @param constraint the child's new constraint
+ */
+public void setConstraint(IFigure child, Object constraint) {
+    invalidate(child);
+}
+
+/**
+ * Sets isObservingVisibility to the given value.
+ * @param newValue <code>true</code> if visibility should be observed
+ */
+public void setObserveVisibility(bool newValue) {
+    if (isObservingVisibility_ is newValue)
+        return;
+    isObservingVisibility_ = newValue;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/AbstractLocator.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,139 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.AbstractLocator;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.Locator;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.PositionConstants;
+
+/**
+ * Places a figure relative to a point determined by the subclass. The figure may be
+ * placed in some location relative to the point with a configurable amount of spacing.
+ */
+public abstract class AbstractLocator
+    : Locator
+{
+
+private int relativePosition = PositionConstants.CENTER;
+private int gap;
+
+/**
+ * Creates a new AbstractLocator.
+ */
+public this() { }
+
+/**
+ * Returns the number of pixels to leave between the figure being located and the
+ * reference point. Only used if {@link #getRelativePosition()} returns something other
+ * than {@link PositionConstants#CENTER}.
+ *
+ * @return The gap
+ * @since 2.0
+ */
+public int getGap() {
+    return gap;
+}
+
+/**
+ * Returns the reference point in absolute coordinates used to calculate the location.
+ * @return The reference point in absolute coordinates
+ * @since 2.0
+ */
+protected abstract Point getReferencePoint();
+
+/**
+ * Recalculate the location of the figure according to its desired position relative to
+ * the center point.
+ *
+ * @param size The size of the figure
+ * @param center The center point
+ * @return The new bounds
+ * @since 2.0
+ */
+protected Rectangle getNewBounds(Dimension size, Point center) {
+    Rectangle bounds = new Rectangle(center, size);
+
+    bounds.x -= bounds.width / 2;
+    bounds.y -= bounds.height / 2;
+
+    int xFactor = 0, yFactor = 0;
+    int position = getRelativePosition();
+
+    if ((position & PositionConstants.NORTH) !is 0)
+        yFactor = -1;
+    else if ((position & PositionConstants.SOUTH) !is 0)
+        yFactor = 1;
+
+    if ((position & PositionConstants.WEST) !is 0)
+        xFactor = -1;
+    else if ((position & PositionConstants.EAST) !is 0)
+        xFactor = 1;
+
+    bounds.x += xFactor * (bounds.width / 2 + getGap());
+    bounds.y += yFactor * (bounds.height / 2 + getGap());
+
+    return bounds;
+}
+
+/**
+ * Returns the position of the figure with respect to the center point. Possible values
+ * can be found in {@link PositionConstants} and include CENTER, NORTH, SOUTH, EAST, WEST,
+ * NORTH_EAST, NORTH_WEST, SOUTH_EAST, or SOUTH_WEST.
+ *
+ * @return An int constant representing the relative position
+ * @since 2.0
+ */
+public int getRelativePosition() {
+    return relativePosition;
+}
+
+/**
+ * Recalculates the position of the figure and returns the updated bounds.
+ * @param target The figure to relocate
+ */
+public void relocate(IFigure target) {
+    Dimension prefSize = target.getPreferredSize();
+    Point center = getReferencePoint();
+    target.translateToRelative(center);
+    target.setBounds(getNewBounds(prefSize, center));
+}
+
+/**
+ * Sets the gap between the reference point and the figure being placed. Only used if
+ * getRelativePosition() returns something other than  {@link PositionConstants#CENTER}.
+ *
+ * @param i The gap
+ * @since 2.0
+ */
+public void setGap(int i) {
+    gap = i;
+}
+
+/**
+ * Sets the position of the figure with respect to the center point. Possible values can
+ * be found in {@link PositionConstants} and include CENTER, NORTH, SOUTH, EAST, WEST,
+ * NORTH_EAST, NORTH_WEST, SOUTH_EAST, or SOUTH_WEST.
+ *
+ * @param pos The relative position
+ * @since 2.0
+ */
+public void setRelativePosition(int pos) {
+    relativePosition = pos;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/AbstractRouter.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.AbstractRouter;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.Connection;
+import dwtx.draw2d.ConnectionRouter;
+
+/**
+ * Base class for implementing a connection router. This class provides stubs for
+ * constraint usage, and some utility methods.
+ */
+public abstract class AbstractRouter
+    : ConnectionRouter
+{
+
+private static const Point START;
+private static const Point END;
+
+static this(){
+    START = new Point();
+    END = new Point();
+}
+
+/**
+ * Returns the constraint for the given Connection.
+ *
+ * @param connection The connection
+ * @return The constraint
+ * @since 2.0
+ */
+public Object getConstraint(Connection connection) {
+    return null;
+}
+
+/**
+ * A convenience method for obtaining a connection's endpoint.  The connection's endpoint
+ * is a point in absolute coordinates obtained by using its source and target {@link
+ * ConnectionAnchor}. The returned Point is a static singleton that is reused to reduce
+ * garbage collection. The caller may modify this point in any way. However, the point
+ * will be reused and its values overwritten during the next call to this method.
+ *
+ * @param connection The connection
+ * @return The endpoint
+ * @since 2.0
+ */
+protected Point getEndPoint(Connection connection) {
+    Point ref_ = connection.getSourceAnchor().getReferencePoint();
+    return END.setLocation(connection.getTargetAnchor().getLocation(ref_));
+}
+
+/**
+ * A convenience method for obtaining a connection's start point.  The connection's
+ * startpoint is a point in absolute coordinates obtained by using its source and target
+ * {@link ConnectionAnchor}. The returned Point is a static singleton that is reused to
+ * reduce garbage collection. The caller may modify this point in any way. However, the
+ * point will be reused and its values overwritten during the next call to this method.
+ *
+ * @param conn The connection
+ * @return The start point
+ * @since 2.0
+ */
+protected Point getStartPoint(Connection conn) {
+    Point ref_ = conn.getTargetAnchor().getReferencePoint();
+    return START.setLocation(conn.getSourceAnchor().getLocation(ref_));
+}
+
+/**
+ * Causes the router to discard any cached information about the given Connection.
+ *
+ * @param connection The connection to invalidate
+ * @since 2.0
+ */
+public void invalidate(Connection connection) { }
+
+/**
+ * Removes the given Connection from this routers list of Connections it is responsible
+ * for.
+ *
+ * @param connection The connection to remove
+ * @since 2.0
+ */
+public void remove(Connection connection) { }
+
+/**
+ * Sets the constraint for the given Connection.
+ *
+ * @param connection The connection
+ * @param constraint The constraint
+ * @since 2.0
+ */
+public void setConstraint(Connection connection, Object constraint) { }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/AccessibleBase.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.AccessibleBase;
+
+import dwt.dwthelper.utils;
+
+/**
+ * The base class for accessible objects which provides accesibilty clients with a unique
+ * ID.
+ */
+public class AccessibleBase {
+
+/**
+ * Returns the id of this accessible object using {@link Object#toHash()}.  
+ * @return the id
+ */
+public final int getAccessibleID() {
+    /* This assumes that the native implementation of toHash in Object is to
+     * return the pointer to the Object, which should be U-unique.
+     */
+    int value = super.toHash();
+    /*
+     * Values -3, -2, and -1 are reserved by DWT's ACC class to have special meaning.
+     * Therefore, a child cannot have this value.
+     */
+    if (value < 0)
+        value -= 4;
+    return value;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ActionEvent.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ActionEvent;
+
+import dwt.dwthelper.utils;
+
+/**
+ * An event that occurs as a result of an action being performed.
+ */
+public class ActionEvent
+    : /+java.util.+/EventObject
+{
+
+private String actionName;
+
+/**
+ * Constructs a new ActionEvent with <i>source</i> as the source of the event.
+ *
+ * @param source The source of the event
+ */
+public this(Object source) {
+    super(source);
+}
+
+/**
+ * Constructs a new ActionEvent with <i>source</i> as the source of the event and
+ * <i>name</i> as the name of the action that was performed.
+ *
+ * @param source The source of the event
+ * @param name The name of the action
+ */
+public this(Object source, String name) {
+    super(source);
+    actionName = name;
+}
+
+/**
+ * Returns the name of the action that was performed.
+ *
+ * @return String The name of the action
+ */
+public String getActionName() {
+    return actionName;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ActionListener.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ActionListener;
+
+import dwt.dwthelper.utils;
+import tango.core.Traits;
+import tango.core.Tuple;
+
+import dwtx.draw2d.ActionEvent;
+
+/**
+ * A Listener interface for receiving {@link ActionEvent ActionEvents}.
+ */
+public interface ActionListener {
+
+/**
+ * Called when the action occurs.
+ * @param event The event
+ */
+void actionPerformed(ActionEvent event);
+
+}
+// DWT Helper
+private class _DgActionListenerT(Dg,T...) : ActionListener {
+
+    alias ParameterTupleOf!(Dg) DgArgs;
+    static assert( is(DgArgs == Tuple!(ActionEvent,T)),
+                "Delegate args not correct" );
+
+    Dg dg;
+    T  t;
+
+    private this( Dg dg, T t ){
+        this.dg = dg;
+        static if( T.length > 0 ){
+            this.t = t;
+        }
+    }
+
+    void actionPerformed( ActionEvent e ){
+        dg(e,t);
+    }
+}
+
+ActionListener dgActionListener( Dg, T... )( Dg dg, T args ){
+    return new _DgActionListenerT!( Dg, T )( dg, args );
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/AncestorHelper.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,189 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.AncestorHelper;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Bean;
+
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.FigureListener;
+import dwtx.draw2d.AncestorListener;
+
+/**
+ * A helper object which tracks the parent chain hierarchy.
+ * @since 2.1
+ */
+class AncestorHelper
+    : PropertyChangeListener, FigureListener
+{
+
+/**
+ * The base figure whose ancestor chain is being observed.
+ */
+protected final IFigure base;
+/**
+ * The array of ancestor listeners.
+ */
+protected AncestorListener[] listeners;
+
+/**
+ * Constructs a new helper on the given base figure and starts listening to figure and
+ * property changes on the base figure's parent chain.  When no longer needed, the helper
+ * should be disposed.
+ * @since 2.1
+ * @param baseFigure
+ */
+public this(IFigure baseFigure) {
+    this.base = baseFigure;
+    addAncestors(baseFigure);
+}
+
+/**
+ * Appends a new listener to the list of listeners.
+ * @param listener the listener
+ */
+public void addAncestorListener(AncestorListener listener) {
+    if (listeners is null) {
+        listeners = new AncestorListener[1];
+        listeners[0] = listener;
+    } else {
+        int oldSize = listeners.length;
+        AncestorListener newListeners[] = new AncestorListener[oldSize + 1];
+        SimpleType!(AncestorListener).arraycopy(listeners, 0, newListeners, 0, oldSize);
+        newListeners[oldSize] = listener;
+        listeners = newListeners;
+    }
+}
+
+/**
+ * Hooks up internal listeners used for maintaining the proper figure listeners.
+ * @param rootFigure the root figure
+ */
+protected void addAncestors(IFigure rootFigure) {
+    for (IFigure ancestor = rootFigure;
+            ancestor !is null;
+            ancestor = ancestor.getParent()) {
+        ancestor.addFigureListener(this);
+        ancestor.addPropertyChangeListener("parent", this); //$NON-NLS-1$
+    }
+}
+
+/**
+ * Removes all internal listeners.
+ */
+public void dispose() {
+    removeAncestors(base);
+    listeners = null;
+}
+
+/**
+ * @see dwtx.draw2d.FigureListener#figureMoved(dwtx.draw2d.IFigure)
+ */
+public void figureMoved(IFigure ancestor) {
+    fireAncestorMoved(ancestor);
+}
+
+/**
+ * Fires notification to the listener list
+ * @param ancestor the figure which moved
+ */
+protected void fireAncestorMoved(IFigure ancestor) {
+    if (listeners is null)
+        return;
+    for (int i = 0; i < listeners.length; i++)
+        listeners[i].ancestorMoved(ancestor);
+}
+
+/**
+ * Fires notification to the listener list
+ * @param ancestor the figure which moved
+ */
+protected void fireAncestorAdded(IFigure ancestor) {
+    if (listeners is null)
+        return;
+    for (int i = 0; i < listeners.length; i++)
+        listeners[i].ancestorAdded(ancestor);
+}
+
+/**
+ * Fires notification to the listener list
+ * @param ancestor the figure which moved
+ */
+protected void fireAncestorRemoved(IFigure ancestor) {
+    if (listeners is null)
+        return;
+    for (int i = 0; i < listeners.length; i++)
+        listeners[i].ancestorRemoved(ancestor);
+}
+
+/**
+ * Returns the total number of listeners.
+ * @return the number of listeners
+ */
+public bool isEmpty() {
+    return listeners is null;
+}
+
+/**
+ * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
+ */
+public void propertyChange(PropertyChangeEvent event) {
+    if (event.getPropertyName().equals("parent")) { //$NON-NLS-1$
+        IFigure oldParent = cast(IFigure)event.getOldValue();
+        IFigure newParent = cast(IFigure)event.getNewValue();
+        if (oldParent !is null) {
+            removeAncestors(oldParent);
+            fireAncestorRemoved(oldParent);
+        }
+        if (newParent !is null) {
+            addAncestors(newParent);
+            fireAncestorAdded(newParent);
+        }
+    }
+}
+
+/**
+ * Removes the first occurence of the given listener
+ * @param listener the listener to remove
+ */
+public void removeAncestorListener(AncestorListener listener) {
+    if (listeners is null)
+        return;
+    for (int index = 0; index < listeners.length; index++)
+        if (listeners[index] is listener) {
+            int newSize = listeners.length - 1;
+            AncestorListener newListeners[] = null;
+            if (newSize !is 0) {
+                newListeners = new AncestorListener[newSize];
+                SimpleType!(AncestorListener).arraycopy(listeners, 0, newListeners, 0, index);
+                SimpleType!(AncestorListener).arraycopy(listeners, index + 1, newListeners, index, newSize - index);
+            }
+            listeners = newListeners;
+            return;
+        }
+}
+
+/**
+ * Unhooks listeners starting at the given figure
+ * @param rootFigure
+ */
+protected void removeAncestors(IFigure rootFigure) {
+    for (IFigure ancestor = rootFigure;
+                ancestor !is null;
+                ancestor = ancestor.getParent()) {
+        ancestor.removeFigureListener(this);
+        ancestor.removePropertyChangeListener("parent", this); //$NON-NLS-1$
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/AncestorListener.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.AncestorListener;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.ConnectionAnchor;
+
+/**
+ * Classes which implement this interface provide methods to respond to changes in the
+ * ancestor properties of a Figure.
+ * <P>
+ * Instances of this class can be added as listeners to a figure using the
+ * <code>addAncestorListener</code> method and removed using the
+ * <code>removeAncestoreListener</code> method.  When the parent chain of the figure being
+ * observed changes or moves, the listener will be notified appropriately.
+ */
+public interface AncestorListener {
+
+/**
+ * Called when an ancestor has been added into the listening figure's hierarchy.
+ * @param ancestor The ancestor that was added
+ */
+void ancestorAdded(IFigure ancestor);
+
+/**
+ * Called when an ancestor has moved to a new location.
+ * @param ancestor The ancestor that has moved
+ */
+void ancestorMoved(IFigure ancestor);
+
+/**
+ * Called when an ancestor has been removed from the listening figure's hierarchy.
+ * @param ancestor The ancestor that has been removed
+ */
+void ancestorRemoved(IFigure ancestor);
+
+/**
+ * An empty implementation of AncestorListener for convenience.
+ */
+class Stub : AncestorListener {
+    public void ancestorMoved(IFigure ancestor) { }
+    public void ancestorAdded(IFigure ancestor) { }
+    public void ancestorRemoved(IFigure ancestor) { }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/AnchorListener.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.AnchorListener;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.ConnectionAnchor;
+
+/**
+ * Classes which implement this interface provide a method to notify that an anchor has
+ * moved.
+ * <P>
+ * Instances of this class can be added as listeners of an Anchor using the
+ * <code>addAnchorListener</code> method and removed using the
+ * <code>removeAnchorListener</code> method.
+ */
+public interface AnchorListener {
+
+/**
+ * Called when an anchor has moved to a new location.
+ * @param anchor The anchor that has moved.
+ */
+void anchorMoved(ConnectionAnchor anchor);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Animation.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,278 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.Animation;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.Animator;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.UpdateManager;
+
+
+/**
+ * A utility for coordinating figure animations. During animation, multiple
+ * <i>animators</i> are employed to capture the <em>initial</em> and <em>final</em> states
+ * for one or more figures. The animators then playback the animation by interpolating the
+ * intermediate states for each figure using the initial and final "keyframes".
+ * <P>
+ * An animator is usually stateless and represents an specific technique. Any state
+ * information is stored by the Animation utility. Therefore, one instance can be used
+ * with multiple figures. Animators hook into the validation mechanism for figures and
+ * connections. These hooks are used to capture the states, and to intercept the typical
+ * layout process to insert the interpolated state.
+ * <P>
+ * To indicate that animation is desired, clients must call {@link #markBegin()} prior to
+ * invalidating any figures that are to be included in the animation. After this method is
+ * called, changes are made, and {@link #run()} is invoked. The run method will force a
+ * validation pass to capture the final states, and then commence the animation. The
+ * animation is synchronous and the method does not return until the animation has
+ * completed.
+ * @see LayoutAnimator
+ * @since 3.2
+ */
+public class Animation {
+
+static class AnimPair {
+
+    const Animator animator;
+    const IFigure figure;
+
+    this(Animator animator, IFigure figure) {
+        this.animator = animator;
+        this.figure = figure;
+    }
+
+    public override int opEquals(Object obj) {
+        AnimPair pair = cast(AnimPair)obj;
+        return pair.animator is animator && pair.figure is figure;
+    }
+
+    public override hash_t toHash() {
+        return (cast(Object)animator).toHash() ^ (cast(Object)figure).toHash();
+    }
+}
+private static const int DEFAULT_DELAY = 250;
+private static Set figureAnimators;
+private static Map finalStates;
+
+private static Map initialStates;
+private static const int PLAYBACK = 3;
+private static float progress;
+private static const int RECORD_FINAL = 2;
+
+private static const int RECORD_INITIAL = 1;
+private static long startTime;
+private static int state;
+private static Set toCapture;
+
+private static UpdateManager updateManager;
+
+private static void capture() {
+    Iterator keys = figureAnimators.iterator();
+    while (keys.hasNext()) {
+        AnimPair pair = cast(AnimPair) keys.next();
+        if (toCapture.contains(pair))
+            pair.animator.capture(pair.figure);
+        else
+            keys.remove();
+    }
+}
+
+static void cleanup() {
+    if (figureAnimators !is null) {
+        Iterator keys = figureAnimators.iterator();
+        while (keys.hasNext()) {
+            AnimPair pair = cast(AnimPair) keys.next();
+            pair.animator.tearDown(pair.figure);
+        }
+    }
+
+    state = 0;
+    step();
+    //Allow layout to occur normally
+    //updateManager.performUpdate();
+
+    initialStates = null;
+    finalStates = null;
+    figureAnimators = null;
+    updateManager = null;
+    toCapture = null;
+    state = 0;
+}
+
+private static void doRun(int duration) {
+    state = RECORD_FINAL;
+    findUpdateManager();
+    updateManager.performValidation();
+    capture();
+    state = PLAYBACK;
+    progress = 0.1f;
+    startTime = System.currentTimeMillis();
+
+    notifyPlaybackStarting();
+
+    while (progress !is 0) {
+        step();
+        updateManager.performUpdate();
+        if (progress is 1.0)
+            progress = 0;
+        else {
+            int delta = cast(int)(System.currentTimeMillis() - startTime);
+            if (delta >= duration)
+                progress = 1f;
+            else
+                progress = 0.1f + 0.9f * delta / duration;
+        }
+    }
+}
+
+private static void findUpdateManager() {
+    AnimPair pair = cast(AnimPair) figureAnimators.iterator().next();
+    updateManager = pair.figure.getUpdateManager();
+}
+
+/**
+ * Returns the final animation state for the given figure.
+ * @param animator the animator for the figure
+ * @param figure the figure being animated
+ * @return the final state
+ * @since 3.2
+ */
+public static Object getFinalState(Animator animator, IFigure figure) {
+    return finalStates.get(new AnimPair(animator, figure));
+}
+
+/**
+ * Returns the initial animation state for the given animator and figure. If no state was
+ * recorded, <code>null</code> is returned.
+ * @param animator the animator for the figure
+ * @param figure the figure being animated
+ * @return the initial state
+ * @since 3.2
+ */
+public static Object getInitialState(Animator animator, IFigure figure) {
+    return initialStates.get(new AnimPair(animator, figure));
+}
+
+/**
+ * Returns the animation progress, where 0.0 < progress &#8804; 1.0.
+ * @return the progress of the animation
+ * @since 3.2
+ */
+public static float getProgress() {
+    return progress;
+}
+
+static void hookAnimator(IFigure figure, Animator animator) {
+    AnimPair pair = new AnimPair(animator, figure);
+    if (figureAnimators.add(pair))
+        animator.init(figure);
+}
+
+static void hookNeedsCapture(IFigure figure, Animator animator) {
+    AnimPair pair = new AnimPair(animator, figure);
+    if (figureAnimators.contains(pair))
+        toCapture.add(pair);
+}
+
+static bool hookPlayback(IFigure figure, Animator animator) {
+    if (toCapture.contains(new AnimPair(animator, figure)))
+        return animator.playback_package(figure);
+    return false;
+}
+
+/**
+ * Returns <code>true</code> if animation is in progress.
+ * @return <code>true</code> when animating
+ * @since 3.2
+ */
+public static bool isAnimating() {
+    return state is PLAYBACK;
+}
+
+static bool isFinalRecording() {
+    return state is RECORD_FINAL;
+}
+
+static bool isInitialRecording() {
+    return state is RECORD_INITIAL;
+}
+
+/**
+ * Marks the beginning of the animation process. If the beginning has already been marked,
+ * this has no effect.
+ * @return returns <code>true</code> if beginning was not previously marked
+ * @since 3.2
+ */
+public static bool markBegin() {
+    if (state is 0) {
+        state = RECORD_INITIAL;
+        initialStates = new HashMap();
+        finalStates = new HashMap();
+        figureAnimators = new HashSet();
+        toCapture = new HashSet();
+        return true;
+    }
+    return false;
+}
+
+private static void notifyPlaybackStarting() {
+    Iterator keys = figureAnimators.iterator();
+    while (keys.hasNext()) {
+        AnimPair pair = cast(AnimPair) keys.next();
+        pair.animator.playbackStarting(pair.figure);
+    }
+}
+
+static void putFinalState(Animator animator, IFigure key, Object state) {
+    finalStates.put(new AnimPair(animator, key), state);
+}
+
+static void putInitialState(Animator animator, IFigure key, Object state) {
+    initialStates.put(new AnimPair(animator, key), state);
+}
+
+/**
+ * Runs animation using the recommended duration: 250 milliseconds.
+ * @see #run(int)
+ * @since 3.2
+ */
+public static void run() {
+    run(DEFAULT_DELAY);
+}
+
+/**
+ * Captures the final states for the animation and then plays the animation.
+ * @param duration the length of animation in milliseconds
+ * @since 3.2
+ */
+public static void run(int duration) {
+    if (state is 0)
+        return;
+    try {
+        if (!figureAnimators.isEmpty())
+            doRun(duration);
+    } finally {
+        cleanup();
+    }
+}
+
+private static void step() {
+    Iterator iter = initialStates.keySet().iterator();
+    while (iter.hasNext())
+        (cast(AnimPair)iter.next()).figure.revalidate();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Animator.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.Animator;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Animation;
+
+/**
+ * Animates some aspect of a figure. Each animator will capture some of the effects of
+ * validation of the figures.
+ * <P>
+ * Animators must be hooked to figure in special ways. Refer to each implementation for
+ * the specific requirements. Animators are generally stateless, which allows them to be
+ * shared and prevents them from leaking memory.
+ *
+ * @since 3.2
+ */
+public abstract class Animator {
+
+this() { }
+
+/**
+ * Captures the final state of the given figure. This method is called once after the
+ * update manager has completed validation of all invalid figures.
+ * @param figure the container
+ * @since 3.2
+ */
+public void capture(IFigure figure) {
+    recordFinalState(figure);
+}
+
+/**
+ * Returns an object encapsulating the current state of the figure. This method is called
+ * to capture both the initial and final states.
+ * @param figure the figure
+ * @return the current state
+ * @since 3.2
+ */
+protected abstract Object getCurrentState(IFigure figure);
+
+/**
+ * Plays back the animation for the given figure and returns <code>true</code> if
+ * successful. This method does nothing by default and return <code>false</code>.
+ * @param figure the figure being animated
+ * @return <code>true</code> if playback was successful
+ * @since 3.2
+ */
+protected bool playback(IFigure figure) {
+    return false;
+}
+package bool playback_package(IFigure figure) {
+    return playback(figure);
+}
+
+/**
+ * Sent as playback is starting for a given figure.
+ * @param figure the figure
+ * @since 3.2
+ */
+public void playbackStarting(IFigure figure) { }
+
+/**
+ * Records the final state information for a figure.
+ * @param figure the figure
+ * @since 3.2
+ */
+protected void recordFinalState(IFigure figure) {
+    Animation.putFinalState(this, figure, getCurrentState(figure));
+}
+
+/**
+ * Records initial state information for the given figure.
+ * @param figure the container.
+ * @since 3.2
+ */
+protected void recordInitialState(IFigure figure) {
+    Animation.putInitialState(this, figure, getCurrentState(figure));
+}
+
+/**
+ * Sets up the animator for the given figure to be animated. This method is called exactly
+ * once time prior to any layouts happening. The animator can capture the figure's current
+ * state, and set any animation-time settings for the figure. Changes made to the figure
+ * should be reverted in {@link #tearDown(IFigure)}.
+ * @param figure the animated figure
+ * @since 3.2
+ */
+public void init(IFigure figure) {
+    recordInitialState(figure);
+}
+
+/**
+ * Reverts any temporary changes made to the figure during animation. This method is
+ * called exactly once after all animation has been completed. Subclasses should extend
+ * this method to revert any changes.
+ * @param figure the animated figure
+ * @since 3.2
+ * @see #init(IFigure)
+ */
+public void tearDown(IFigure figure) { }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ArrowButton.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ArrowButton;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.Button;
+import dwtx.draw2d.Orientable;
+import dwtx.draw2d.Triangle;
+import dwtx.draw2d.MarginBorder;
+import dwtx.draw2d.ColorConstants;
+
+/**
+ * A Button which displays a triangle pointing in a specified direction. This class is
+ * used by the {@link ScrollBar} figure.
+ */
+public class ArrowButton
+    : Button
+    , Orientable
+{
+
+/**
+ * Constructs a default ArrowButton with the arrow pointing north.
+ *
+ * @since 2.0
+ */
+public this() {
+    createTriangle();
+    setRequestFocusEnabled(false);
+    setFocusTraversable(false);
+}
+
+/**
+ * Constructs an ArrowButton with the arrow having the direction given in the input.
+ * The direction can be one of many directional constants defined in
+ * {@link PositionConstants}.
+ *
+ * @param direction  Direction of the arrow
+ * @since 2.0
+ */
+public this(int direction) {
+    this();
+    setDirection(direction);
+}
+
+/**
+ * Contructs a triangle with a black background pointing north, and sets it as the
+ * contents of the button.
+ *
+ * @since 2.0
+ */
+protected void createTriangle() {
+    Triangle tri = new Triangle();
+    tri.setOutline(true);
+    tri.setBackgroundColor(ColorConstants.listForeground);
+    tri.setForegroundColor(ColorConstants.listForeground);
+    tri.setBorder(new MarginBorder(new Insets(2)));
+    setContents(tri);
+}
+
+/**
+ * @see dwtx.draw2d.Orientable#setDirection(int)
+ */
+public void setDirection(int value) {
+    setChildrenDirection(value);
+}
+
+/**
+ * @see dwtx.draw2d.Orientable#setOrientation(int)
+ */
+public void setOrientation(int value) {
+    setChildrenOrientation(value);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ArrowLocator.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ArrowLocator;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.ConnectionLocator;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Connection;
+import dwtx.draw2d.RotatableDecoration;
+
+/**
+ * Locator used to place a {@link RotatableDecoration} on a {@link Connection}. The
+ * decoration can be placed at the source or target end of the connection figure. The
+ * default connection implementation uses a {@link DelegatingLayout} which requires
+ * locators.
+ */
+public class ArrowLocator : ConnectionLocator {
+
+/**
+ * Constructs an ArrowLocator associated with passed connection and tip location (either
+ * {@link ConnectionLocator#SOURCE} or {@link ConnectionLocator#TARGET}).
+ *
+ * @param connection The connection associated with the locator
+ * @param location Location of the arrow decoration
+ * @since 2.0
+ */
+public this(Connection connection, int location) {
+    super(connection, location);
+}
+
+/**
+ * Relocates the passed in figure (which must be a {@link RotatableDecoration}) at either
+ * the start or end of the connection.
+ * @param target The RotatableDecoration to relocate
+ */
+public void relocate(IFigure target) {
+    PointList points = getConnection().getPoints();
+    RotatableDecoration arrow = cast(RotatableDecoration)target;
+    arrow.setLocation(getLocation(points));
+
+    if (getAlignment() is SOURCE)
+        arrow.setReferencePoint(points.getPoint(1));
+    else if (getAlignment() is TARGET)
+        arrow.setReferencePoint(points.getPoint(points.size() - 2));
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/AutomaticRouter.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,223 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.AutomaticRouter;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.internal.MultiValueMap;
+import dwtx.draw2d.AbstractRouter;
+import dwtx.draw2d.ConnectionRouter;
+import dwtx.draw2d.ConnectionAnchor;
+import dwtx.draw2d.Connection;
+
+/**
+ * An abstract router implementation which detects when multiple connections are
+ * overlapping. Two connections overlap if the combination of source and target
+ * anchors are equal. Subclasses must implement {@link #handleCollision(PointList, int)}
+ * to determine how to avoid the overlap.
+ * <p>
+ * This router can delegate to another connection router. The wrappered router will route
+ * the connections first, after which overlapping will be determined.
+ */
+public abstract class AutomaticRouter
+    : AbstractRouter
+{
+
+private ConnectionRouter nextRouter;
+private MultiValueMap connections;
+
+public this(){
+    connections = new MultiValueMap();
+}
+
+private class HashKey {
+
+    private ConnectionAnchor anchor1, anchor2;
+
+    this(Connection conn) {
+        anchor1 = conn.getSourceAnchor();
+        anchor2 = conn.getTargetAnchor();
+    }
+
+    public override int opEquals(Object object) {
+        bool isEqual = false;
+        HashKey hashKey;
+
+        if (auto hashKey = cast(HashKey)object ) {
+            ConnectionAnchor hkA1 = hashKey.getFirstAnchor();
+            ConnectionAnchor hkA2 = hashKey.getSecondAnchor();
+
+            isEqual = ((cast(Object)hkA1).opEquals(cast(Object)anchor1) && (cast(Object)hkA2).opEquals(cast(Object)anchor2))
+                || ((cast(Object)hkA1).opEquals(cast(Object)anchor2) && (cast(Object)hkA2).opEquals(cast(Object)anchor1));
+        }
+        return isEqual;
+    }
+
+    public ConnectionAnchor getFirstAnchor() {
+        return anchor1;
+    }
+
+    public ConnectionAnchor getSecondAnchor() {
+        return anchor2;
+    }
+
+    public override hash_t toHash() {
+        return (cast(Object)anchor1).toHash() ^ (cast(Object)anchor2).toHash();
+    }
+}
+
+/**
+ * @see dwtx.draw2d.ConnectionRouter#getConstraint(Connection)
+ */
+public Object getConstraint(Connection connection) {
+    if (next() !is null)
+        return next().getConstraint(connection);
+    return null;
+}
+
+/**
+ * Handles collisions between 2 or more Connections. Collisions are currently defined as 2
+ * connections with no bendpoints and whose start and end points coincide.  In other
+ * words, the 2 connections are the exact same line.
+ *
+ * @param list The PointList of a connection that collides with another connection
+ * @param index The index of the current connection in the list of colliding connections
+ */
+protected abstract void handleCollision(PointList list, int index);
+
+/**
+ * @see dwtx.draw2d.ConnectionRouter#invalidate(Connection)
+ */
+public void invalidate(Connection conn) {
+    if (next() !is null)
+        next().invalidate(conn);
+    if (conn.getSourceAnchor() is null || conn.getTargetAnchor() is null)
+        return;
+    HashKey connectionKey = new HashKey(conn);
+    ArrayList connectionList = connections.get(connectionKey);
+    int affected = connections.remove(connectionKey, cast(Object)conn);
+    if (affected !is -1) {
+        for (int i = affected; i < connectionList.size(); i++)
+            (cast(Connection)connectionList.get(i)).revalidate();
+    } else
+        connections.removeValue(cast(Object)conn);
+
+}
+
+/**
+ * Returns the next router in the chain.
+ * @return The next router
+ * @since 2.0
+ */
+protected ConnectionRouter next() {
+    return nextRouter;
+}
+
+
+
+/**
+ * @see dwtx.draw2d.ConnectionRouter#remove(Connection)
+ */
+public void remove(Connection conn) {
+    if (conn.getSourceAnchor() is null || conn.getTargetAnchor() is null)
+        return;
+    HashKey connectionKey = new HashKey(conn);
+    ArrayList connectionList = connections.get(connectionKey);
+    if (connectionList !is null) {
+        int index = connections.remove(connectionKey,cast(Object) conn);
+        for (int i = index + 1; i < connectionList.size(); i++)
+            (cast(Connection)connectionList.get(i)).revalidate();
+    }
+    if (next() !is null)
+        next().remove(conn);
+}
+
+/**
+ * Routes the given connection.  Calls the 'next' router first (if one exists) and if no
+ * bendpoints were added by the next router, collisions are dealt with by calling
+ * {@link #handleCollision(PointList, int)}.
+ * @param conn The connection to route
+ */
+public void route(Connection conn) {
+    if (next() !is null)
+        next().route(conn);
+    else {
+        conn.getPoints().removeAllPoints();
+        setEndPoints(conn);
+    }
+
+    if (conn.getPoints().size() is 2) {
+        PointList points = conn.getPoints();
+        HashKey connectionKey = new HashKey(conn);
+        ArrayList connectionList = connections.get(connectionKey);
+
+        if (connectionList !is null) {
+
+            int index;
+
+            if (connectionList.contains(cast(Object)conn)) {
+                index = connectionList.indexOf(cast(Object)conn) + 1;
+            } else {
+                index = connectionList.size() + 1;
+                connections.put(connectionKey, cast(Object)conn);
+            }
+
+            handleCollision(points, index);
+            conn.setPoints(points);
+        } else {
+            connections.put(connectionKey, cast(Object)conn);
+        }
+    }
+}
+
+/**
+ * An AutomaticRouter needs no constraints for the connections it routes.  This method
+ * invalidates the connections and calls {@link #setConstraint(Connection, Object)} on the
+ * {@link #next()} router.
+ * @see dwtx.draw2d.ConnectionRouter#setConstraint(Connection, Object)
+ */
+public void setConstraint(Connection connection, Object constraint) {
+    invalidate(connection);
+    if (next() !is null)
+        next().setConstraint(connection, constraint);
+}
+
+/**
+ * Sets the start and end points for the given connection.
+ * @param conn The connection
+ */
+protected void setEndPoints(Connection conn) {
+    PointList points = conn.getPoints();
+    points.removeAllPoints();
+    Point start = getStartPoint(conn);
+    Point end = getEndPoint(conn);
+    conn.translateToRelative(start);
+    conn.translateToRelative(end);
+    points.addPoint(start);
+    points.addPoint(end);
+    conn.setPoints(points);
+}
+
+/**
+ * Sets the next router.
+ * @param router The ConnectionRouter
+ * @since 2.0
+ */
+public void setNextRouter(ConnectionRouter router) {
+    nextRouter = router;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Bendpoint.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.Bendpoint;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+
+/**
+ * A helper used to calculate the point at which a {@link dwtx.draw2d.Connection}
+ * should bend. A bendpoint returns a point <em>relative</em> to the connection figure on
+ * which it is being used. This was chosen so that fixed bendpoints would be easy to
+ * implement. A fixed bendpoint will have a fixed x and y value.  Although the absolute x
+ * and y location change during zoom and scrolling, the relative values stay the same.
+ */
+public interface Bendpoint {
+
+/**
+ * Returns the location of the bendpoint <em>relative</em> to the connection. The returned
+ * value may be by reference. The caller should NOT modify the returned value.
+ * 
+ * @return the location of the bendpoint relative to the Connection
+ */
+Point getLocation();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/BendpointConnectionRouter.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.BendpointConnectionRouter;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.geometry.PrecisionPoint;
+import dwtx.draw2d.AbstractRouter;
+import dwtx.draw2d.Connection;
+import dwtx.draw2d.Bendpoint;
+
+/**
+ * Routes {@link Connection}s through a <code>List</code> of {@link Bendpoint Bendpoints}.
+ */
+public class BendpointConnectionRouter
+    : AbstractRouter
+{
+
+private Map constraints;
+private static const PrecisionPoint A_POINT;
+
+static this(){
+    A_POINT = new PrecisionPoint();
+}
+
+public this(){
+    constraints = new HashMap(11);
+}
+
+/**
+ * Gets the constraint for the given {@link Connection}.
+ *
+ * @param connection The connection whose constraint we are retrieving
+ * @return The constraint
+ */
+public Object getConstraint(Connection connection) {
+    return constraints.get(cast(Object)connection);
+}
+
+/**
+ * Removes the given connection from the map of constraints.
+ *
+ * @param connection The connection to remove
+ */
+public void remove(Connection connection) {
+    constraints.remove(cast(Object)connection);
+}
+
+/**
+ * Routes the {@link Connection}.  Expects the constraint to be a List
+ * of {@link dwtx.draw2d.Bendpoint Bendpoints}.
+ *
+ * @param conn The connection to route
+ */
+public void route(Connection conn) {
+    PointList points = conn.getPoints();
+    points.removeAllPoints();
+
+    List bendpoints = cast(List)getConstraint(conn);
+    if (bendpoints is null)
+        bendpoints = Collections.EMPTY_LIST;
+
+    Point ref1, ref2;
+
+    if (bendpoints.isEmpty()) {
+        ref1 = conn.getTargetAnchor().getReferencePoint();
+        ref2 = conn.getSourceAnchor().getReferencePoint();
+    } else {
+        ref1 = new Point((cast(Bendpoint)bendpoints.get(0)).getLocation());
+        conn.translateToAbsolute(ref1);
+        ref2 = new Point((cast(Bendpoint)bendpoints.get(bendpoints.size() - 1)).getLocation());
+        conn.translateToAbsolute(ref2);
+    }
+
+    A_POINT.setLocation(conn.getSourceAnchor().getLocation(ref1));
+    conn.translateToRelative(A_POINT);
+    points.addPoint(A_POINT);
+
+    for (int i = 0; i < bendpoints.size(); i++) {
+        Bendpoint bp = cast(Bendpoint)bendpoints.get(i);
+        points.addPoint(bp.getLocation());
+    }
+
+    A_POINT.setLocation(conn.getTargetAnchor().getLocation(ref2));
+    conn.translateToRelative(A_POINT);
+    points.addPoint(A_POINT);
+    conn.setPoints(points);
+}
+
+/**
+ * Sets the constraint for the given {@link Connection}.
+ *
+ * @param connection The connection whose constraint we are setting
+ * @param constraint The constraint
+ */
+public void setConstraint(Connection connection, Object constraint) {
+    constraints.put(cast(Object)connection, constraint);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/BendpointLocator.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.BendpointLocator;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.ConnectionLocator;
+import dwtx.draw2d.Connection;
+
+/**
+ * Places a figure relative to a specified bend in a {@link Connection}. A bendpoint is
+ * one of the points returned in the connection's {@link Connection#getPoints()} method.
+ * It is not related to the bendpoint class used as routing constraints.
+ */
+public class BendpointLocator
+    : ConnectionLocator
+{
+
+private int index;
+
+/**
+ * Creates a BendpointLocator associated with passed Connection c and index i.
+ *
+ * @param c Connection associated with BendpointLocator
+ * @param i Index of bendpoint, represents the position of the bendpoint on Connection c
+ * @since 2.0
+ */
+public this(Connection c, int i) {
+    super(c);
+    index = i;
+}
+
+/**
+ * Returns the index of this BendpointLocator. This index is the position of the reference
+ * point in this BendpointLocator's {@link Connection}.
+ *
+ * @return The index
+ * @since 2.0
+ */
+protected int getIndex() {
+    return index;
+}
+
+/**
+ * Returns reference point associated with the BendpointLocator.  This Point is taken from
+ * the BendpointLocator's connection and is point number 'index'.
+ *
+ * @return The reference point
+ * @since 2.0
+ */
+protected Point getReferencePoint() {
+    Point p = getConnection().getPoints().getPoint(Point.SINGLETON, getIndex());
+    getConnection().translateToAbsolute(p);
+    return p;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Border.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.Border;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Graphics;
+
+/**
+ * A decoration on a Figure. A border may paint itself within the bounds of a figure, and
+ * it may provide Insets which can affect how the figures children are posiiton and
+ * painted.
+ * <P>
+ * A border instance may be used with multiple figure instances.
+ */
+public interface Border {
+
+/**
+ * Returns the Insets for this Border for the given Figure.
+ * @param figure The figure this border belongs to
+ * @return The insets
+ */
+Insets getInsets(IFigure figure);
+
+/**
+ * Returns the preferred width and height that this border would like to display itself
+ * properly.
+ * @param figure The figure
+ * @return The preferred size
+ */
+Dimension getPreferredSize(IFigure figure);
+
+/**
+ * Returns <code>true</code> if the Border completely fills the region defined in
+ * {@link #paint(IFigure, Graphics, Insets)}.
+ * @return <code>true</code> if this border is opaque
+ */
+bool isOpaque();
+
+/**
+ * Paints the border. The border should paint inside figure's {@link IFigure#getBounds()},
+ * inset by the parameter <i>insets</i>.  The border generally should not paint inside its
+ * own insets.  More specifically, Border <i>b</i> should paint inside the rectangle:
+ * figure.getBounds().getCropped(insets) and outside of the rectangle:
+ * figure.getBounds().getCropped(insets).getCropped(getInsets()) where <i>inside</i> is
+ * defined as {@link Rectangle#contains(int, int)}.
+ *
+ * @param figure The figure this border belongs to
+ * @param graphics The graphics object used for painting
+ * @param insets The insets
+ */
+void paint(IFigure figure, Graphics graphics, Insets insets);
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/BorderLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,327 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.BorderLayout;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.AbstractHintLayout;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.PositionConstants;
+
+/**
+ * @author Pratik Shah
+ */
+public class BorderLayout
+    : AbstractHintLayout
+{
+
+/**
+ * Constant to be used as a constraint for child figures
+ */
+public static const Integer CENTER;
+/**
+ * Constant to be used as a constraint for child figures
+ */
+public static const Integer TOP;
+/**
+ * Constant to be used as a constraint for child figures
+ */
+public static const Integer BOTTOM;
+/**
+ * Constant to be used as a constraint for child figures
+ */
+public static const Integer LEFT;
+/**
+ * Constant to be used as a constraint for child figures
+ */
+public static const Integer RIGHT;
+
+static this(){
+    CENTER = new Integer(PositionConstants.CENTER);
+    TOP = new Integer(PositionConstants.TOP);
+    BOTTOM = new Integer(PositionConstants.BOTTOM);
+    LEFT = new Integer(PositionConstants.LEFT);
+    RIGHT = new Integer(PositionConstants.RIGHT);
+}
+
+private IFigure center, left, top, bottom, right;
+private int vGap = 0, hGap = 0;
+
+/**
+ * @see dwtx.draw2d.AbstractHintLayout#calculateMinimumSize(IFigure, int, int)
+ */
+protected Dimension calculateMinimumSize(IFigure container, int wHint, int hHint) {
+    int minWHint = 0, minHHint = 0;
+    if (wHint < 0) {
+        minWHint = -1;
+    }
+    if (hHint < 0) {
+        minHHint = -1;
+    }
+    Insets border = container.getInsets();
+    wHint = Math.max(minWHint, wHint - border.getWidth());
+    hHint = Math.max(minHHint, hHint - border.getHeight());
+    Dimension minSize = new Dimension();
+    int middleRowWidth = 0, middleRowHeight = 0;
+    int rows = 0, columns = 0;
+
+    if (top !is null  && top.isVisible()) {
+        Dimension childSize = top.getMinimumSize(wHint, hHint);
+        hHint = Math.max(minHHint, hHint - (childSize.height + vGap));
+        minSize.setSize(childSize);
+        rows += 1;
+    }
+    if (bottom !is null && bottom.isVisible()) {
+        Dimension childSize = bottom.getMinimumSize(wHint, hHint);
+        hHint = Math.max(minHHint, hHint - (childSize.height + vGap));
+        minSize.width = Math.max(minSize.width, childSize.width);
+        minSize.height += childSize.height;
+        rows += 1;
+    }
+    if (left !is null && left.isVisible()) {
+        Dimension childSize = left.getMinimumSize(wHint, hHint);
+        middleRowWidth = childSize.width;
+        middleRowHeight = childSize.height;
+        wHint = Math.max(minWHint, wHint - (childSize.width + hGap));
+        columns += 1;
+    }
+    if (right !is null  && right.isVisible()) {
+        Dimension childSize = right.getMinimumSize(wHint, hHint);
+        middleRowWidth += childSize.width;
+        middleRowHeight = Math.max(childSize.height, middleRowHeight);
+        wHint = Math.max(minWHint, wHint - (childSize.width + hGap));
+        columns += 1;
+    }
+    if (center !is null && center.isVisible()) {
+        Dimension childSize = center.getMinimumSize(wHint, hHint);
+        middleRowWidth += childSize.width;
+        middleRowHeight = Math.max(childSize.height, middleRowHeight);
+        columns += 1;
+    }
+
+    rows += columns > 0 ? 1 : 0;
+    // Add spacing, insets, and the size of the middle row
+    minSize.height += middleRowHeight + border.getHeight() + ((rows - 1) * vGap);
+    minSize.width = Math.max(minSize.width, middleRowWidth) + border.getWidth()
+                    + ((columns - 1) * hGap);
+
+    return minSize;
+}
+
+/**
+ * @see AbstractLayout#calculatePreferredSize(IFigure, int, int)
+ */
+protected Dimension calculatePreferredSize(IFigure container, int wHint, int hHint) {
+    int minWHint = 0, minHHint = 0;
+    if (wHint < 0)
+        minWHint = -1;
+
+    if (hHint < 0)
+        minHHint = -1;
+
+    Insets border = container.getInsets();
+    wHint = Math.max(minWHint, wHint - border.getWidth());
+    hHint = Math.max(minHHint, hHint - border.getHeight());
+    Dimension prefSize = new Dimension();
+    int middleRowWidth = 0, middleRowHeight = 0;
+    int rows = 0, columns = 0;
+
+    if (top !is null && top.isVisible()) {
+        Dimension childSize = top.getPreferredSize(wHint, hHint);
+        hHint = Math.max(minHHint, hHint - (childSize.height + vGap));
+        prefSize.setSize(childSize);
+        rows += 1;
+    }
+    if (bottom !is null && bottom.isVisible()) {
+        Dimension childSize = bottom.getPreferredSize(wHint, hHint);
+        hHint = Math.max(minHHint, hHint - (childSize.height + vGap));
+        prefSize.width = Math.max(prefSize.width, childSize.width);
+        prefSize.height += childSize.height;
+        rows += 1;
+    }
+    if (left !is null && left.isVisible()) {
+        Dimension childSize = left.getPreferredSize(wHint, hHint);
+        middleRowWidth = childSize.width;
+        middleRowHeight = childSize.height;
+        wHint = Math.max(minWHint, wHint - (childSize.width + hGap));
+        columns += 1;
+    }
+    if (right !is null && right.isVisible()) {
+        Dimension childSize = right.getPreferredSize(wHint, hHint);
+        middleRowWidth += childSize.width;
+        middleRowHeight = Math.max(childSize.height, middleRowHeight);
+        wHint = Math.max(minWHint, wHint - (childSize.width + hGap));
+        columns += 1;
+    }
+    if (center !is null && center.isVisible()) {
+        Dimension childSize = center.getPreferredSize(wHint, hHint);
+        middleRowWidth += childSize.width;
+        middleRowHeight = Math.max(childSize.height, middleRowHeight);
+        columns += 1;
+    }
+
+    rows += columns > 0 ? 1 : 0;
+    // Add spacing, insets, and the size of the middle row
+    prefSize.height += middleRowHeight + border.getHeight() + ((rows - 1) * vGap);
+    prefSize.width = Math.max(prefSize.width, middleRowWidth) + border.getWidth()
+                    + ((columns - 1) * hGap);
+
+    return prefSize;
+}
+
+/**
+ * @see dwtx.draw2d.LayoutManager#layout(IFigure)
+ */
+public void layout(IFigure container) {
+    Rectangle area = container.getClientArea();
+    Rectangle rect = new Rectangle();
+
+    Dimension childSize;
+
+    if (top !is null && top.isVisible()) {
+        childSize = top.getPreferredSize(area.width, -1);
+        rect.setLocation(area.x, area.y);
+        rect.setSize(childSize);
+        rect.width = area.width;
+        top.setBounds(rect);
+        area.y += rect.height + vGap;
+        area.height -= rect.height + vGap;
+    }
+    if (bottom !is null && bottom.isVisible()) {
+        childSize = bottom.getPreferredSize(Math.max(area.width, 0), -1);
+        rect.setSize(childSize);
+        rect.width = area.width;
+        rect.setLocation(area.x, area.y + area.height - rect.height);
+        bottom.setBounds(rect);
+        area.height -= rect.height + vGap;
+    }
+    if (left !is null && left.isVisible()) {
+        childSize = left.getPreferredSize(-1, Math.max(0, area.height));
+        rect.setLocation(area.x, area.y);
+        rect.width = childSize.width;
+        rect.height = Math.max(0, area.height);
+        left.setBounds(rect);
+        area.x += rect.width + hGap;
+        area.width -= rect.width + hGap;
+    }
+    if (right !is null && right.isVisible()) {
+        childSize = right.getPreferredSize(-1, Math.max(0, area.height));
+        rect.width = childSize.width;
+        rect.height = Math.max(0, area.height);
+        rect.setLocation(area.x + area.width - rect.width, area.y);
+        right.setBounds(rect);
+        area.width -= rect.width + hGap;
+    }
+    if (center !is null && center.isVisible()) {
+        if (area.width < 0)
+            area.width = 0;
+        if (area.height < 0)
+            area.height = 0;
+        center.setBounds(area);
+    }
+}
+
+/**
+ * @see dwtx.draw2d.AbstractLayout#remove(IFigure)
+ */
+public void remove(IFigure child) {
+    if (center is child) {
+        center = null;
+    } else if (top is child) {
+        top = null;
+    } else if (bottom is child) {
+        bottom = null;
+    } else if (right is child) {
+        right = null;
+    } else if (left is child) {
+        left = null;
+    }
+}
+
+/**
+ * Sets the location of hte given child in this layout.  Valid constraints:
+ * <UL>
+ *      <LI>{@link #CENTER}</LI>
+ *      <LI>{@link #TOP}</LI>
+ *      <LI>{@link #BOTTOM}</LI>
+ *      <LI>{@link #LEFT}</LI>
+ *      <LI>{@link #RIGHT}</LI>
+ *      <LI><code>null</code> (to remove a child's constraint)</LI>
+ * </UL>
+ *
+ * <p>
+ * Ensure that the given Figure is indeed a child of the Figure on which this layout has
+ * been set.  Proper behaviour cannot be guaranteed if that is not the case.  Also ensure
+ * that every child has a valid constraint.
+ * </p>
+ * <p>
+ * Passing a <code>null</code> constraint will invoke {@link #remove(IFigure)}.
+ * </p>
+ * <p>
+ * If the given child was assigned another constraint earlier, it will be re-assigned to
+ * the new constraint.  If there is another child with the given constraint, it will be
+ * over-ridden so that the given child now has that constraint.
+ * </p>
+ *
+ * @see dwtx.draw2d.AbstractLayout#setConstraint(IFigure, Object)
+ */
+public void setConstraint(IFigure child, Object constraint) {
+    remove(child);
+    super.setConstraint(child, constraint);
+    if (constraint is null) {
+        return;
+    }
+
+    switch ((cast(Integer) constraint).intValue()) {
+        case PositionConstants.CENTER :
+            center = child;
+            break;
+        case PositionConstants.TOP :
+            top = child;
+            break;
+        case PositionConstants.BOTTOM :
+            bottom = child;
+            break;
+        case PositionConstants.RIGHT :
+            right = child;
+            break;
+        case PositionConstants.LEFT :
+            left = child;
+            break;
+        default :
+            break;
+    }
+}
+
+/**
+ * Sets the horizontal spacing to be used between the children.  Default is 0.
+ *
+ * @param gap The horizonal spacing
+ */
+public void setHorizontalSpacing(int gap) {
+    hGap = gap;
+}
+
+/**
+ * Sets the vertical spacing ot be used between the children.  Default is 0.
+ *
+ * @param gap The vertical spacing
+ */
+public void setVerticalSpacing(int gap) {
+    vGap = gap;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/BufferedGraphicsSource.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.BufferedGraphicsSource;
+
+import dwt.dwthelper.utils;
+
+
+
+import dwt.DWT;
+import dwt.DWTError;
+import dwt.graphics.GC;
+import dwt.graphics.Image;
+static import dwt.graphics.Point;
+import dwt.widgets.Canvas;
+import dwt.widgets.Caret;
+import dwt.widgets.Control;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.GraphicsSource;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.SWTGraphics;
+
+class BufferedGraphicsSource : GraphicsSource {
+
+private Image imageBuffer;
+private GC imageGC;
+private GC controlGC;
+private Control control;
+private Rectangle inUse;
+
+/**
+ * Constructs a new buffered graphics source using the given control.
+ * @since 2.1
+ * @param c the control
+ */
+public this(Control c) {
+    control = c;
+}
+
+/**
+ * @see dwtx.draw2d.GraphicsSource#flushGraphics(dwtx.draw2d.geometry.Rectangle)
+ */
+public void flushGraphics(Rectangle region) {
+    if (inUse.isEmpty())
+        return;
+
+    bool restoreCaret = false;
+    Canvas canvas = null;
+    if (auto canvas = cast(Canvas)control ) {
+        Caret caret = canvas.getCaret();
+        if (caret !is null)
+            restoreCaret = caret.isVisible();
+        if (restoreCaret)
+            caret.setVisible(false);
+    }
+    /*
+     * The imageBuffer may be null if double-buffering was not successful.
+     */
+    if (imageBuffer !is null) {
+        imageGC.dispose();
+        controlGC.drawImage(getImage(),
+                0, 0, inUse.width, inUse.height,
+                inUse.x, inUse.y, inUse.width, inUse.height);
+        imageBuffer.dispose();
+        imageBuffer = null;
+        imageGC = null;
+    }
+    controlGC.dispose();
+    controlGC = null;
+
+    if (restoreCaret)
+        canvas.getCaret().setVisible(true);
+}
+
+/**
+ * @see dwtx.draw2d.GraphicsSource#getGraphics(dwtx.draw2d.geometry.Rectangle)
+ */
+public Graphics getGraphics(Rectangle region) {
+    if (control is null || control.isDisposed())
+        return null;
+
+    dwt.graphics.Point.Point ptSWT = control.getSize();
+    inUse = new Rectangle(0, 0, ptSWT.x, ptSWT.y);
+    inUse.intersect(region);
+    if (inUse.isEmpty())
+        return null;
+
+    /*
+     * Bugzilla 53632 - Attempts to create large images on some platforms will fail.
+     * When this happens, do not use double-buffering for painting.
+     */
+    try {
+        imageBuffer = new Image(null, inUse.width, inUse.height);
+    } catch (DWTError noMoreHandles) {
+        imageBuffer = null;
+    } catch (IllegalArgumentException tooBig) {
+        imageBuffer = null;
+    }
+
+    controlGC = new GC(control,
+            control.getStyle() & (DWT.RIGHT_TO_LEFT | DWT.LEFT_TO_RIGHT));
+    Graphics graphics;
+    if (imageBuffer !is null) {
+        imageGC = new GC(imageBuffer,
+                control.getStyle() & (DWT.RIGHT_TO_LEFT | DWT.LEFT_TO_RIGHT));
+        imageGC.setBackground(controlGC.getBackground());
+        imageGC.setForeground(controlGC.getForeground());
+        imageGC.setFont(controlGC.getFont());
+        imageGC.setLineStyle(controlGC.getLineStyle());
+        imageGC.setLineWidth(controlGC.getLineWidth());
+        imageGC.setXORMode(controlGC.getXORMode());
+        graphics = new SWTGraphics(imageGC);
+        graphics.translate(inUse.getLocation().negate());
+    } else {
+        graphics = new SWTGraphics(controlGC);
+    }
+
+    graphics.setClip(inUse);
+    return graphics;
+}
+
+/**
+ * Returns the current image buffer or <code>null</code>.
+ * @since 2.1
+ * @return the current image buffer
+ */
+protected Image getImage() {
+    return imageBuffer;
+}
+
+/**
+ * Returns the current GC used on the buffer or <code>null</code>.
+ * @since 2.1
+ * @return the GC for the image buffer
+ */
+protected GC getImageGC() {
+    return imageGC;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Button.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.Button;
+
+import dwt.dwthelper.utils;
+
+import dwt.graphics.Image;
+import dwtx.draw2d.Clickable;
+import dwtx.draw2d.ColorConstants;
+import dwtx.draw2d.Label;
+
+/**
+ * A Button usually has a border and appears to move up and down in response to being
+ * pressed. It can contain  an image and/or text in it.
+ */
+public class Button
+    : Clickable
+{
+
+/**
+ * Constructs a default Button with no icon or text.
+ *
+ * @since 2.0
+ */
+public this() {
+    super();
+    setStyle(STYLE_BUTTON);
+}
+
+/**
+ * Contructs a Button containing the icon <i>image</i>.
+ *
+ * @param image  Image to be used by the Button as its icon.
+ * @since 2.0
+ */
+public this(Image image) {
+    super(new Label(image), STYLE_BUTTON);
+}
+
+/**
+ * Constructs a Button containing the given text.
+ *
+ * @param text  Text for the button.
+ * @since 2.0
+ */
+public this(String text) {
+    super(new Label(text), STYLE_BUTTON);
+}
+
+/**
+ * Constructs a Button with the given image and text.
+ *
+ * @param text  Text for the button.
+ * @param image  Image for the button.
+ * @since 2.0
+ */
+public this(String text, Image image) {
+    super(new Label(text, image), STYLE_BUTTON);
+}
+
+/**
+ * Initializes this button by setting its default border and setting its background color
+ * to {@link ColorConstants#button}.
+ *
+ * @since 2.0
+ */
+protected void init() {
+    super.init();
+    setBackgroundColor(ColorConstants.button);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ButtonBorder.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,280 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ButtonBorder;
+
+import dwt.dwthelper.utils;
+
+
+
+import dwt.graphics.Color;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.SchemeBorder;
+import dwtx.draw2d.Border;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.Clickable;
+import dwtx.draw2d.ButtonModel;
+import dwtx.draw2d.ColorConstants;
+
+/**
+ * Creates a border for a clickable type of figure, which works in conjunction with the
+ * Figure and its model. This border adjusts itself to the various states the model of the
+ * figure could be. This border uses an extended  {@link SchemeBorder.Scheme Scheme}
+ * called {@link ButtonScheme} which provides more information required by border to
+ * handle the the states of the model.
+ *
+ * @see Scheme
+ * @see ButtonScheme
+ */
+public class ButtonBorder
+    : SchemeBorder
+{
+    alias SchemeBorder.paint paint;
+/**
+ * Default button border.
+ * @see SCHEMES#BUTTON
+ */
+public static const Border BUTTON;
+/**
+ * Inverted hightlight colors from BUTTON.
+ * @see SCHEMES#BUTTON_CONTRAST
+ */
+public static const Border BUTTON_CONTRAST;
+/**
+ * Used for scrollbar buttons.
+ * @see SCHEMES#BUTTON_SCROLLBAR
+ */
+public static const Border BUTTON_SCROLLBAR;
+/**
+ * Used for toolbar buttons.
+ * @see SCHEMES#TOOLBAR
+ */
+public static const Border TOOLBAR;
+
+static this(){
+    BUTTON = new ButtonBorder(SCHEMES.BUTTON);
+    BUTTON_CONTRAST = new ButtonBorder(SCHEMES.BUTTON_CONTRAST);
+    BUTTON_SCROLLBAR = new ButtonBorder(SCHEMES.BUTTON_SCROLLBAR);
+    TOOLBAR = new ButtonBorder(SCHEMES.TOOLBAR);
+}
+
+/**
+ * Provides for a scheme to represent the borders of clickable figures like buttons.
+ * Though similar to the {@link SchemeBorder.Scheme Scheme} it supports an extra set of
+ * borders for the pressed states.
+ */
+public static class ButtonScheme
+    : Scheme
+{
+    private Color[]
+        highlightPressed = null,
+        shadowPressed = null;
+
+    /**
+     * Constructs a new button scheme where the input colors are the colors for the
+     * top-left and bottom-right sides of the  border. These colors serve as the colors
+     * when the border is in a pressed state too. The width of each side is determined by
+     * the number of colors passed in as input.
+     *
+     * @param highlight  Colors for the top-left sides of the border
+     * @param shadow     Colors for the bottom-right sides of the border
+     * @since 2.0
+     */
+    public this(Color[] highlight, Color[] shadow) {
+        highlightPressed = this.highlight = highlight;
+        shadowPressed = this.shadow = shadow;
+        init();
+    }
+
+    /**
+     * Constructs a new button scheme where the input colors are the colors for the
+     * top-left and bottom-right sides of the  border, for the normal and pressed states.
+     * The width of  each side is determined by the number of colors passed in  as input.
+     *
+     * @param hl   Colors for the top-left sides of the border
+     * @param sh   Colors for the bottom-right sides of the border
+     * @param hlp  Colors for the top-left sides of the border when figure is pressed
+     * @param shp  Colors for the bottom-right sides of the border when figure is pressed
+     * @since 2.0
+     */
+    public this(Color[] hl, Color[] sh, Color[] hlp, Color[] shp) {
+        highlight = hl;
+        shadow = sh;
+        highlightPressed = hlp;
+        shadowPressed = shp;
+        init();
+    }
+
+    /**
+     * Calculates and returns the Insets for this border. The calculations are based on
+     * the number of normal and pressed, highlight and shadow colors.
+     *
+     * @return  The insets for this border
+     * @since 2.0
+     */
+    protected Insets calculateInsets() {
+        int br = 1 + Math.max(getShadow().length, getHighlightPressed().length);
+        int tl = Math.max(getHighlight().length, getShadowPressed().length);
+        return new Insets(tl, tl, br, br);
+    }
+
+    /**
+     * Calculates and returns the opaque state of this border.
+     * <p>
+     * Returns false in the following conditions:
+     * <ul>
+     *      <li> The number of highlight colors is different than the the number of
+     *      shadow colors.
+     *      <li> The number of pressed highlight colors is different than the number of
+     *      pressed shadow colors.
+     *      <li> Any of the highlight and shadow colors are set to <code>null</code>
+     *      <li> Any of the pressed highlight and shadow colors are set to
+     *      <code>null</code>
+     * </ul>
+     * This is done so that the entire region under the figure is properly covered.
+     *
+     * @return  The opaque state of this border
+     * @since 2.0
+     */
+    protected bool calculateOpaque() {
+        if (!super.calculateOpaque())
+            return false;
+        if (getHighlight().length !is getShadowPressed().length)
+            return false;
+        if (getShadow().length !is getHighlightPressed().length)
+            return false;
+        Color [] colors = getHighlightPressed();
+        for (int i = 0; i < colors.length; i++)
+            if (colors[i] is null)
+                return false;
+        colors = getShadowPressed();
+        for (int i = 0; i < colors.length; i++)
+            if (colors[i] is null)
+                return false;
+        return true;
+    }
+
+    /**
+     * Returns the pressed highlight colors of this border.
+     *
+     * @return  Colors as an array of Colors
+     * @since 2.0
+     */
+    protected Color[] getHighlightPressed() {
+        return highlightPressed;
+    }
+
+    /**
+     * Returns the pressed shadow colors of this border.
+     *
+     * @return  Colors as an array of Colors
+     * @since 2.0
+     */
+    protected Color[] getShadowPressed() {
+        return shadowPressed;
+    }
+}
+
+/**
+ * Interface defining commonly used schemes for the ButtonBorder.
+ */
+public struct SCHEMES {
+
+    /**
+     * Contrast button scheme
+     */
+    static const ButtonScheme BUTTON_CONTRAST;
+    /**
+     * Regular button scheme
+     */
+    static const ButtonScheme BUTTON;
+    /**
+     * Toolbar button scheme
+     */
+    static const ButtonScheme TOOLBAR;
+    /**
+     * Scrollbar button scheme
+     */
+    static const ButtonScheme BUTTON_SCROLLBAR;
+static this(){
+    BUTTON_CONTRAST = new ButtonScheme(
+        [ColorConstants.button, ColorConstants.buttonLightest],
+        DARKEST_DARKER
+    );
+    BUTTON = new ButtonScheme(
+        [ColorConstants.buttonLightest],
+        DARKEST_DARKER
+    );
+    TOOLBAR = new ButtonScheme(
+        [ColorConstants.buttonLightest],
+        [ColorConstants.buttonDarker]
+    );
+    BUTTON_SCROLLBAR = new ButtonScheme(
+        [ColorConstants.button, ColorConstants.buttonLightest],
+        DARKEST_DARKER,
+        [ColorConstants.buttonDarker],
+        [ColorConstants.buttonDarker]
+    );
+}
+}
+
+/**
+ * Constructs a ButtonBorder with a predefined button scheme set as its default.
+ *
+ * @since 2.0
+ */
+public this() {
+    setScheme(SCHEMES.BUTTON);
+}
+
+/**
+ * Constructs a ButtonBorder with the input ButtonScheme set as its Scheme.
+ *
+ * @param scheme  ButtonScheme for this ButtonBorder.
+ * @since 2.0
+ */
+public this(ButtonScheme scheme) {
+    setScheme(scheme);
+}
+
+/**
+ * Paints this border with the help of the set scheme, the model of the clickable figure,
+ * and other inputs. The scheme is used in conjunction with the state of the model to get
+ * the appropriate colors for the border.
+ *
+ * @param figure The Clickable that this border belongs to
+ * @param graphics The graphics used for painting
+ * @param insets The insets
+ */
+public void paint(IFigure figure, Graphics graphics, Insets insets) {
+    Clickable clickable = cast(Clickable)figure;
+    ButtonModel model = clickable.getModel();
+    ButtonScheme colorScheme = cast(ButtonScheme)getScheme();
+
+    if (clickable.isRolloverEnabled() && !model.isMouseOver()
+        && !model.isSelected())
+        return;
+
+    Color[] tl, br;
+    if (model.isSelected() || model.isArmed()) {
+        tl = colorScheme.getShadowPressed();
+        br = colorScheme.getHighlightPressed();
+    } else {
+        tl = colorScheme.getHighlight();
+        br = colorScheme.getShadow();
+    }
+
+    paint(graphics, figure, insets, tl, br);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ButtonGroup.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,231 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ButtonGroup;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Bean;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.ButtonModel;
+
+/**
+ * A ButtonGroup holds a group of {@link Clickable Clickable's} models and provides unique
+ * selection in them. There is  capability to add a default selection. Models who want to
+ * belong to the group should just add themselves to this group. By doing so they listen
+ * to this group for changes.
+ * <p>
+ * Setting of the default selection results in its being selected any time
+ * {@link #setSelected(ButtonModel, bool)} is called. If no default selection is set,
+ * the last entry selected is not allowed to deselect.
+ */
+public class ButtonGroup {
+
+private ButtonModel selectedModel;
+private ButtonModel defaultModel;
+private List members;
+private List listeners;
+
+/**
+ * Constructs a ButtonGroup with no default selection.
+ *
+ * @since 2.0
+ */
+public this() {
+    members = new ArrayList();
+    listeners = new ArrayList();
+}
+
+/**
+ * Adds the passed ButtonModel to the ButtonGroup.
+ *
+ * @param model  ButtonModel to be added to this group
+ * @since 2.0
+ */
+public void add(ButtonModel model) {
+    if (!members.contains(model)) {
+        model.setGroup(this);
+        members.add(model);
+    }
+}
+
+/**
+ * Adds the passed listener. ButtonGroups use PropertyChangeListeners to react to
+ * selection changes in the ButtonGroup.
+ *
+ * @param listener  Listener to be added to this group
+ * @since 2.0
+ */
+public void addPropertyChangeListener(PropertyChangeListener listener) {
+    listeners.add(cast(Object)listener);
+}
+
+/**
+ * Fires a PropertyChangeEvent to all PropertyChangeListeners added to this ButtonGroup.
+ *
+ * @param oldValue  Old selection value
+ * @param newValue  New selection value
+ * @since 2.0
+ */
+protected void firePropertyChange(Object oldValue, Object newValue) {
+    PropertyChangeEvent event = new PropertyChangeEvent(
+        this, ButtonModel.SELECTED_PROPERTY, oldValue, newValue);
+    for (int i = 0; i < listeners.size(); i++)
+        (cast(PropertyChangeListener)listeners.get(i)).propertyChange(event);
+}
+
+/**
+ * Returns the ButtonModel which is selected by default for this ButtonGroup.
+ *
+ * @return The default ButtonModel
+ * @since 2.0
+ */
+public ButtonModel getDefault() {
+    return defaultModel;
+}
+
+/**
+ * Returns a List which contains all of the {@link ButtonModel ButtonModels} added to this
+ * ButtonGroup.
+ *
+ * @return The List of ButtonModels in this ButtonGroup
+ * @since 2.0
+ */
+public List getElements() {
+    return members;
+}
+
+/**
+ * Returns the ButtonModel for the currently selected button.
+ *
+ * @return The ButtonModel for the currently selected button
+ * @since 2.0
+ */
+public ButtonModel getSelected() {
+    return selectedModel;
+}
+
+/**
+ * Determines if the given ButtonModel is selected or not.
+ *
+ * @param model  Model being tested for selected status
+ * @return  Selection state of the given model
+ * @since 2.0
+ */
+public bool isSelected(ButtonModel model) {
+    return (model is getSelected());
+}
+
+/**
+ * Removes the given ButtonModel from this ButtonGroup.
+ *
+ * @param model  ButtonModel being removed
+ * @since 2.0
+ */
+public void remove(ButtonModel model) {
+    if (getSelected() is model)
+        setSelected(getDefault(), true);
+    if (defaultModel is model)
+        defaultModel = null;
+    members.remove(model);
+}
+
+/**
+ * Removes the passed PropertyChangeListener from this ButtonGroup.
+ *
+ * @param listener  PropertyChangeListener to be removed
+ * @since 2.0
+ */
+public void removePropertyChangeListener(PropertyChangeListener listener) {
+    listeners.remove(cast(Object)listener);
+}
+
+/**
+ * Sets the passed ButtonModel to be the currently selected ButtonModel of this
+ * ButtonGroup. Fires a property change.
+ *
+ * @param model ButtonModel to be selected
+ * @since 2.0
+ */
+protected void selectNewModel(ButtonModel model) {
+    ButtonModel oldModel = selectedModel;
+    selectedModel = model;
+    if (oldModel !is null)
+        oldModel.setSelected(false);
+    firePropertyChange(oldModel, model);
+}
+
+/**
+ * Sets the default selection of this ButtonGroup. Does nothing if it is not present in
+ * the group. Sets selection to the passed ButtonModel.
+ *
+ * @param model  ButtonModel which is to be the default selection.
+ * @since 2.0
+ */
+public void setDefault(ButtonModel model) {
+    defaultModel = model;
+    if (getSelected() is null && defaultModel !is null)
+        defaultModel.setSelected(true);
+}
+
+/**
+ * Sets the button with the given ButtonModel to be selected.
+ *
+ * @param model The ButtonModel to be selected
+ * @since 2.0
+ */
+public void setSelected(ButtonModel model) {
+    if (model is null)
+        selectNewModel(null);
+    else
+        model.setSelected(true);
+}
+
+/**
+ * Sets model to the passed state.
+ * <p>
+ * If <i>value</i> is
+ * <ul>
+ *      <li><code>true</code>:
+ *      <ul>
+ *          <li>The passed ButtonModel will own selection.
+ *      </ul>
+ *      <li><code>false</code>:
+ *      <ul>
+ *          <li>If the passed model owns selection, it will lose selection, and
+ *          selection will be given to the default ButonModel. If no default
+ *          ButtonModel was set, selection will remain as it was, as one ButtonModel
+ *          must own selection at all times.
+ *          <li>If the passed model does not own selection, then selection will remain
+ *          as it was.
+ *      </ul>
+ * </ul>
+ *
+ * @param model The model to be affected
+ * @param value The selected state
+ * @since 2.0
+ */
+public void setSelected(ButtonModel model, bool value) {
+    if (value) {
+        if (model is getSelected())
+            return;
+        selectNewModel(model);
+    } else {
+        if (model !is getSelected())
+            return;
+        if (getDefault() is null)
+            return;
+        getDefault().setSelected(true);
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ButtonModel.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,572 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ButtonModel;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+import dwtx.dwtxhelper.Collection;
+import dwtx.dwtxhelper.Timer;
+import dwtx.dwtxhelper.TimerTask;
+static import dwt.widgets.Display;
+
+import dwtx.draw2d.ButtonGroup;
+import dwtx.draw2d.ActionListener;
+import dwtx.draw2d.ChangeListener;
+import dwtx.draw2d.ButtonStateTransitionListener;
+import dwtx.draw2d.EventListenerList;
+import dwtx.draw2d.ActionEvent;
+import dwtx.draw2d.ChangeEvent;
+
+
+//import dwtx.draw2d.internal.Timer;
+
+/**
+ * A model for buttons containing several properties, including enabled, pressed,
+ * selected, rollover enabled and mouseover.
+ */
+public class ButtonModel {
+
+/** Enabled property */
+public static const String ENABLED_PROPERTY = "enabled"; //$NON-NLS-1$
+/** Pressed property */
+public static const String PRESSED_PROPERTY = "pressed"; //$NON-NLS-1$
+/** Selected property */
+public static const String SELECTED_PROPERTY = "selected"; //$NON-NLS-1$
+/** Rollover Enabled property */
+public static const String ROLLOVER_ENABLED_PROPERTY = "rollover enabled"; //$NON-NLS-1$
+/** Mouseover property */
+public static const String MOUSEOVER_PROPERTY = "mouseover"; //$NON-NLS-1$
+
+/** Armed property */
+public static const String ARMED_PROPERTY = "armed";  //$NON-NLS-1$
+
+
+/** Flag for armed button state */
+protected static const int ARMED_FLAG               = 1;
+/** Flag for pressed button state */
+protected static const int PRESSED_FLAG             = 2;
+/** Flag for mouseOver state */
+protected static const int MOUSEOVER_FLAG           = 4;
+/** Flag for selected button state */
+protected static const int SELECTED_FLAG            = 8;
+/** Flag for enablement button state */
+protected static const int ENABLED_FLAG             = 16;
+/** Flag for rollover enablement button state */
+protected static const int ROLLOVER_ENABLED_FLAG    = 32;
+/** Flag that can be used by subclasses to define more states */
+protected static const int MAX_FLAG                 = ROLLOVER_ENABLED_FLAG;
+
+private int state = ENABLED_FLAG;
+private Object data;
+
+/**
+ * Action performed events are not fired until the mouse button is released.
+ */
+public static const int DEFAULT_FIRING_BEHAVIOR = 0;
+
+/**
+ * Action performed events fire repeatedly until the mouse button is released.
+ */
+public static const int REPEAT_FIRING_BEHAVIOR  = 1;
+
+/**
+ * The name of the action associated with this button.
+ */
+protected String actionName;
+
+/**
+ * The ButtonGroup this button belongs to (if any).
+ */
+protected ButtonGroup group = null;
+
+private EventListenerList listeners;
+
+/**
+ * Listens to button state transitions and fires action performed events based on the
+ * desired behavior ({@link #DEFAULT_FIRING_BEHAVIOR} or {@link #REPEAT_FIRING_BEHAVIOR}).
+ */
+protected ButtonStateTransitionListener firingBehavior;
+
+this(){
+    listeners = new EventListenerList();
+    installFiringBehavior();
+}
+
+/**
+ * Registers the given listener as an ActionListener.
+ *
+ * @param listener The ActionListener to add
+ * @since 2.0
+ */
+public void addActionListener(ActionListener listener) {
+    if (listener is null)
+        throw new IllegalArgumentException("");
+    listeners.addListener(ActionListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * Registers the given listener as a ChangeListener.
+ *
+ * @param listener The ChangeListener to add
+ * @since 2.0
+ */
+public void addChangeListener(ChangeListener listener) {
+    if (listener is null)
+        throw new IllegalArgumentException("");
+    listeners.addListener(ChangeListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * Registers the given listener as a ButtonStateTransitionListener.
+ *
+ * @param listener The ButtonStateTransitionListener to add
+ * @since 2.0
+ */
+public void addStateTransitionListener(ButtonStateTransitionListener listener) {
+    if (listener is null)
+        throw new IllegalArgumentException("");
+    listeners.addListener(ButtonStateTransitionListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * Notifies any ActionListeners on this ButtonModel that an action has been performed.
+ *
+ * @since 2.0
+ */
+protected void fireActionPerformed() {
+    Iterator iter = listeners.getListeners(ActionListener.classinfo);
+    ActionEvent action = new ActionEvent(this);
+    while (iter.hasNext())
+        (cast(ActionListener)iter.next()).
+            actionPerformed(action);
+}
+
+/**
+ * Notifies any listening ButtonStateTransitionListener that the pressed state of this
+ * button has been cancelled.
+ *
+ * @since 2.0
+ */
+protected void fireCanceled() {
+    Iterator iter = listeners.getListeners(ButtonStateTransitionListener.classinfo);
+    while (iter.hasNext())
+        (cast(ButtonStateTransitionListener)iter.next()).
+            canceled();
+}
+
+/**
+ * Notifies any listening ButtonStateTransitionListener that this button has been pressed.
+ *
+ * @since 2.0
+ */
+protected void firePressed() {
+    Iterator iter = listeners.getListeners(ButtonStateTransitionListener.classinfo);
+    while (iter.hasNext())
+        (cast(ButtonStateTransitionListener)iter.next()).
+            pressed();
+}
+
+/**
+ * Notifies any listening ButtonStateTransitionListener that this button has been
+ * released.
+ *
+ * @since 2.0
+ */
+protected void fireReleased() {
+    Iterator iter = listeners.getListeners(ButtonStateTransitionListener.classinfo);
+    while (iter.hasNext())
+        (cast(ButtonStateTransitionListener)iter.next()).
+            released();
+}
+
+/**
+ * Notifies any listening ButtonStateTransitionListeners that this button has resumed
+ * activity.
+ *
+ * @since 2.0
+ */
+protected void fireResume() {
+    Iterator iter = listeners.getListeners(ButtonStateTransitionListener.classinfo);
+    while (iter.hasNext())
+        (cast(ButtonStateTransitionListener)iter.next()).
+            resume();
+}
+
+/**
+ * Notifies any listening ChangeListeners that this button's state has changed.
+ *
+ * @param property The name of the property that changed
+ * @since 2.0
+ */
+protected void fireStateChanged(String property) {
+    Iterator iter = listeners.getListeners(ChangeListener.classinfo);
+    ChangeEvent change = new ChangeEvent(this, property);
+    while (iter.hasNext())
+        (cast(ChangeListener)iter.next()).
+            handleStateChanged(change);
+}
+
+/**
+ * Notifies any listening ButtonStateTransitionListeners that this button has suspended
+ * activity.
+ *
+ * @since 2.0
+ */
+protected void fireSuspend() {
+    Iterator iter = listeners.getListeners(ButtonStateTransitionListener.classinfo);
+    while (iter.hasNext())
+        (cast(ButtonStateTransitionListener)iter.next()).
+            suspend();
+}
+
+bool getFlag(int which) {
+    return (state & which) !is 0;
+}
+
+/**
+ * Returns the group to which this model belongs.
+ *
+ * @return The ButtonGroup to which this model belongs
+ * @since 2.0
+ */
+public ButtonGroup getGroup() {
+    return group;
+}
+
+/**
+ * Returns an object representing user data.
+ *
+ * @return User data
+ * @since 2.0
+ */
+public Object getUserData() {
+    return data;
+}
+
+/**
+ * Sets the firing behavior for this button.
+ *
+ * @since 2.0
+ */
+protected void installFiringBehavior() {
+    setFiringBehavior(DEFAULT_FIRING_BEHAVIOR);
+}
+
+/**
+ * Returns <code>true</code> if this button is armed. If a button is armed, it will fire
+ * an ActionPerformed when released.
+ *
+ * @return <code>true</code> if this button is armed
+ * @since 2.0
+ */
+public bool isArmed() {
+    return (state & ARMED_FLAG) !is 0;
+}
+
+/**
+ * Returns <code>true</code> if this button is enabled.
+ *
+ * @return <code>true</code> if this button is enabled
+ * @since 2.0
+ */
+public bool isEnabled() {
+    return (state & ENABLED_FLAG) !is 0;
+}
+
+/**
+ * Returns <code>true</code> if the mouse is over this button.
+ *
+ * @return <code>true</code> if the mouse is over this button
+ * @since 2.0
+ */
+public bool isMouseOver() {
+    return (state & MOUSEOVER_FLAG) !is 0;
+}
+
+/**
+ * Returns <code>true</code> if this button is pressed.
+ *
+ * @return <code>true</code> if this button is pressed
+ * @since 2.0
+ */
+public bool isPressed() {
+    return (state & PRESSED_FLAG) !is 0;
+}
+
+/**
+ * Returns the selection state of this model. If this model belongs to any group, the
+ * group is queried for selection state, else the flags are used.
+ *
+ * @return  <code>true</code> if this button is selected
+ * @since 2.0
+ */
+public bool isSelected() {
+    if (group is null) {
+        return (state & SELECTED_FLAG) !is 0;
+    } else {
+        return group.isSelected(this);
+    }
+}
+
+/**
+ * Removes the given ActionListener.
+ *
+ * @param listener The ActionListener to remove
+ * @since 2.0
+ */
+public void removeActionListener(ActionListener listener) {
+    listeners.removeListener(ActionListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * Removes the given ChangeListener.
+ *
+ * @param listener The ChangeListener to remove
+ * @since 2.0
+ */
+public void removeChangeListener(ChangeListener listener) {
+    listeners.removeListener(ChangeListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * Removes the given ButtonStateTransitionListener.
+ *
+ * @param listener The ButtonStateTransitionListener to remove
+ * @since 2.0
+ */
+public void removeStateTransitionListener(ButtonStateTransitionListener listener) {
+    listeners.removeListener(ButtonStateTransitionListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * Sets this button to be armed. If a button is armed, it will fire an ActionPerformed
+ * when released.
+ *
+ *@param value The armed state
+ * @since 2.0
+ */
+public void setArmed(bool value) {
+    if (isArmed() is value)
+        return;
+    if (!isEnabled())
+        return;
+    setFlag(ARMED_FLAG, value);
+    fireStateChanged(ARMED_PROPERTY);
+}
+
+/**
+ * Sets this button to be enabled.
+ *
+ * @param value The enabled state
+ * @since 2.0
+ */
+public void setEnabled(bool value) {
+    if (isEnabled() is value)
+        return;
+    if (!value) {
+        setMouseOver(false);
+        setArmed(false);
+        setPressed(false);
+    }
+    setFlag(ENABLED_FLAG, value);
+    fireStateChanged(ENABLED_PROPERTY);
+}
+
+/**
+ * Sets the firing behavior for this button. {@link #DEFAULT_FIRING_BEHAVIOR} is the
+ * default behavior, where action performed events are not fired until the mouse button is
+ * released. {@link #REPEAT_FIRING_BEHAVIOR} causes action performed events to fire
+ * repeatedly until the mouse button is released.
+ *
+ * @param type The firing behavior type
+ * @since 2.0
+ *
+ */
+public void setFiringBehavior(int type) {
+    if (firingBehavior !is null)
+        removeStateTransitionListener(firingBehavior);
+    switch (type) {
+        case REPEAT_FIRING_BEHAVIOR:
+            firingBehavior = new RepeatFiringBehavior();
+            break;
+        default:
+            firingBehavior = new DefaultFiringBehavior();
+    }
+    addStateTransitionListener(firingBehavior);
+}
+
+void setFlag(int flag, bool value) {
+    if (value)
+        state |= flag;
+    else
+        state &= ~flag;
+}
+
+/**
+ * Sets the ButtonGroup to which this model belongs to. Adds this model as a listener to
+ * the group.
+ *
+ * @param bg The group to which this model belongs.
+ * @since 2.0
+ */
+public void setGroup(ButtonGroup bg) {
+    if (group is bg)
+        return;
+    if (group !is null)
+        group.remove(this);
+    group = bg;
+    if (group !is null)
+        group.add(this);
+}
+
+/**
+ * Sets the mouseover property of this button.
+ *
+ * @param value The value the mouseover property will be set to
+ * @since 2.0
+ */
+public void setMouseOver(bool value) {
+    if (isMouseOver() is value)
+        return;
+    if (isPressed())
+        if (value)
+            fireResume();
+        else
+            fireSuspend();
+    setFlag(MOUSEOVER_FLAG, value);
+    fireStateChanged(MOUSEOVER_PROPERTY);
+}
+
+/**
+ * Sets the pressed property of this button.
+ *
+ * @param value The value the pressed property will be set to
+ * @since 2.0
+ */
+public void setPressed(bool value) {
+    if (isPressed() is value)
+        return;
+    setFlag(PRESSED_FLAG, value);
+    if (value)
+        firePressed();
+    else {
+        if (isArmed())
+            fireReleased();
+        else
+            fireCanceled();
+    }
+    fireStateChanged(PRESSED_PROPERTY);
+}
+
+/**
+ * Sets this button to be selected.
+ *
+ * @param value The value the selected property will be set to
+ * @since 2.0
+ */
+public void setSelected(bool value) {
+    if (group is null) {
+        if (isSelected() is value)
+            return;
+    } else {
+        group.setSelected(this, value);
+        if (getFlag(SELECTED_FLAG) is isSelected())
+            return;
+    }
+    setFlag(SELECTED_FLAG, value);
+    fireStateChanged(SELECTED_PROPERTY);
+}
+
+/**
+ * Sets user data.
+ *
+ * @param data The user data
+ * @since 2.0
+ */
+public void setUserData(Object data) {
+    this.data = data;
+}
+
+class DefaultFiringBehavior
+    : ButtonStateTransitionListener
+{
+    public void released() {
+        fireActionPerformed();
+    }
+}
+
+class RepeatFiringBehavior
+    : ButtonStateTransitionListener
+{
+    protected static const int
+        INITIAL_DELAY = 250,
+        STEP_DELAY = 40;
+
+    protected int
+        stepDelay = INITIAL_DELAY,
+        initialDelay = STEP_DELAY;
+
+    protected Timer timer;
+
+    public void pressed() {
+        fireActionPerformed();
+        if (!isEnabled())
+            return;
+
+        timer = new Timer();
+        TimerTask runAction = new Task(timer);
+
+        timer.scheduleAtFixedRate(runAction, INITIAL_DELAY, STEP_DELAY);
+    }
+
+    public void canceled() {
+        suspend();
+    }
+    public void released() {
+        suspend();
+    }
+
+    public void resume() {
+        timer = new Timer();
+
+        TimerTask runAction = new Task(timer);
+
+        timer.scheduleAtFixedRate(runAction, STEP_DELAY, STEP_DELAY);
+    }
+
+    public void suspend() {
+        if (timer is null) return;
+        timer.cancel();
+        timer = null;
+    }
+}
+
+class Task
+    : TimerTask {
+
+    private Timer timer;
+
+    public this(Timer timer) {
+        this.timer = timer;
+    }
+
+    public void run() {
+        dwt.widgets.Display.Display.getDefault().syncExec(dgRunnable( {
+                if (!isEnabled())
+                    timer.cancel();
+                fireActionPerformed();
+        }));
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ButtonStateTransitionListener.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ButtonStateTransitionListener;
+
+import dwt.dwthelper.utils;
+
+class ButtonStateTransitionListener {
+
+protected final void cancel() { }
+public void canceled() { }
+final void cancelled() { }
+
+protected final void press() { }
+public void pressed() { }
+
+protected final void release() { }
+public void released() { }
+
+public void resume() { }
+
+public void suspend() { }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ChangeEvent.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ChangeEvent;
+
+import dwt.dwthelper.utils;
+
+/**
+ * An event for property changes.  Includes the source of the event as well as the name of
+ * the property that has changed.
+ */
+public class ChangeEvent
+    : /+java.util.+/EventObject
+{
+
+private String property;
+
+/**
+ * Constructs a new ChangeEvent with the given object as the source of the event.
+ * @param source The source of the event
+ */
+public this(Object source) {
+    super(source);
+}
+
+/**
+ * Constructs a new ChangeEvent with the given source object and property name.
+ * @param source The source of the event
+ * @param property The property name
+ */
+public this(Object source, String property) {
+    super(source);
+    setPropertyName(property);
+}
+
+/**
+ * Returns the name of the property that has changed.
+ * @return String the name of the property that has changed
+ */
+public String getPropertyName() {
+    return property;
+}
+
+/**
+ * Sets the name of the property that has changed.
+ * @param string The property name
+ */
+protected void setPropertyName(String string) {
+    property = string;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ChangeListener.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ChangeListener;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.ChangeEvent;
+import tango.core.Traits;
+import tango.core.Tuple;
+
+/**
+ * A generic state listener
+ */
+public interface ChangeListener {
+
+/**
+ * Called when the listened to object's state has changed.
+ * @param event the ChangeEvent
+ */
+void handleStateChanged(ChangeEvent event);
+
+}
+
+// DWT Helper
+private class _DgChangeListenerT(Dg,T...) : ChangeListener {
+
+    alias ParameterTupleOf!(Dg) DgArgs;
+    static assert( is(DgArgs == Tuple!(ChangeEvent,T)),
+                "Delegate args not correct" );
+
+    Dg dg;
+    T  t;
+
+    private this( Dg dg, T t ){
+        this.dg = dg;
+        static if( T.length > 0 ){
+            this.t = t;
+        }
+    }
+
+    void handleStateChanged( ChangeEvent e ){
+        dg(e,t);
+    }
+}
+
+ChangeListener dgChangeListener( Dg, T... )( Dg dg, T args ){
+    return new _DgChangeListenerT!( Dg, T )( dg, args );
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/CheckBox.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.CheckBox;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.ByteArrayInputStream;
+
+import dwt.graphics.Image;
+import dwt.graphics.ImageData;
+import dwtx.draw2d.Toggle;
+import dwtx.draw2d.Label;
+import dwtx.draw2d.ChangeListener;
+import dwtx.draw2d.ChangeEvent;
+import dwtx.draw2d.ButtonModel;
+
+/**
+ * A Checkbox is a toggle figure which toggles between the checked and unchecked figures
+ * to simulate a check box. A check box contains a text label to represent it.
+ */
+public final class CheckBox
+    : Toggle
+{
+
+private Label label = null;
+
+static const Image
+    UNCHECKED,
+    CHECKED;
+
+static this(){
+    UNCHECKED = createImage( getImportData!("dwtx.draw2d.checkboxenabledoff.gif")); //$NON-NLS-1$
+    CHECKED = createImage( getImportData!("dwtx.draw2d.checkboxenabledon.gif")); //$NON-NLS-1$
+}
+
+private static Image createImage( ImportData importdata ) {
+    Image image = new Image(null, new ImageData(new ByteArrayInputStream( cast(byte[]) importdata.data)));
+    return image;
+}
+
+/**
+ * Constructs a CheckBox with no text.
+ *
+ * @since 2.0
+ */
+public this() {
+    this(""); //$NON-NLS-1$
+}
+
+/**
+ * Constructs a CheckBox with the passed text in its label.
+ * @param text The label text
+ * @since 2.0
+ */
+public this(String text) {
+    setContents(label = new Label(text, UNCHECKED));
+}
+
+/**
+ * Adjusts CheckBox's icon depending on selection status.
+ *
+ * @since 2.0
+ */
+protected void handleSelectionChanged() {
+    if (isSelected())
+        label.setIcon(CHECKED);
+    else
+        label.setIcon(UNCHECKED);
+}
+
+/**
+ * Initializes this Clickable by setting a default model and adding a clickable event
+ * handler for that model. Also adds a ChangeListener to update icon when  selection
+ * status changes.
+ *
+ * @since 2.0
+ */
+protected void init() {
+    super.init();
+    addChangeListener(new class() ChangeListener {
+        public void handleStateChanged(ChangeEvent changeEvent) {
+            if (changeEvent.getPropertyName().equals(ButtonModel.SELECTED_PROPERTY))
+                handleSelectionChanged();
+        }
+    });
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ChopboxAnchor.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ChopboxAnchor;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.AbstractConnectionAnchor;
+
+/**
+ * The ChopboxAnchor's location is found by calculating the intersection of a line drawn
+ * from the center point of its owner's box to a reference point on that box. Thus
+ * {@link Connection Connections} using the ChopBoxAnchor will be oriented such that they
+ * point to their owner's center.
+ */
+public class ChopboxAnchor
+    : AbstractConnectionAnchor
+{
+
+/**
+ * Constructs a new ChopboxAnchor.
+ */
+protected this() { }
+
+/**
+ * Constructs a ChopboxAnchor with the given <i>owner</i> figure.
+ *
+ * @param owner The owner figure
+ * @since 2.0
+ */
+public this(IFigure owner) {
+    super(owner);
+}
+
+/**
+ * Gets a Rectangle from {@link #getBox()} and returns the Point where a line from the
+ * center of the Rectangle to the Point <i>reference</i> intersects the Rectangle.
+ *
+ * @param reference The reference point
+ * @return The anchor location
+ */
+public Point getLocation(Point reference) {
+    Rectangle r = Rectangle.SINGLETON;
+    r.setBounds(getBox());
+    r.translate(-1, -1);
+    r.resize(1, 1);
+
+    getOwner().translateToAbsolute(r);
+    float centerX = r.x + 0.5f * r.width;
+    float centerY = r.y + 0.5f * r.height;
+
+    if (r.isEmpty() || (reference.x is cast(int)centerX && reference.y is cast(int)centerY))
+        return new Point(cast(int)centerX, cast(int)centerY);  //This avoids divide-by-zero
+
+    float dx = reference.x - centerX;
+    float dy = reference.y - centerY;
+
+    //r.width, r.height, dx, and dy are guaranteed to be non-zero.
+    float scale = 0.5f / Math.max(Math.abs(dx) / r.width, Math.abs(dy) / r.height);
+
+    dx *= scale;
+    dy *= scale;
+    centerX += dx;
+    centerY += dy;
+
+    return new Point(Math.round(centerX), Math.round(centerY));
+}
+
+/**
+ * Returns the bounds of this ChopboxAnchor's owner.  Subclasses can override this method
+ * to adjust the box the anchor can be placed on.  For instance, the owner figure may have
+ * a drop shadow that should not be included in the box.
+ *
+ * @return The bounds of this ChopboxAnchor's owner
+ * @since 2.0
+ */
+protected Rectangle getBox() {
+    return getOwner().getBounds();
+}
+
+/**
+ * Returns the anchor's reference point. In the case of the ChopboxAnchor, this is the
+ * center of the anchor's owner.
+ *
+ * @return The reference point
+ */
+public Point getReferencePoint() {
+    Point ref_ = getBox().getCenter();
+    getOwner().translateToAbsolute(ref_);
+    return ref_;
+}
+
+
+/**
+ * Returns <code>true</code> if the other anchor has the same owner and box.
+ * @param obj the other anchor
+ * @return <code>true</code> if equal
+ */
+public override int opEquals(Object obj) {
+    if (auto other = cast(ChopboxAnchor)obj ) {
+        return other.getOwner() is getOwner() && other.getBox().opEquals(getBox());
+    }
+    return false;
+}
+
+/**
+ * The owning figure's hashcode is used since equality is approximately based on the
+ * owner.
+ * @return the hash code.
+ */
+public override hash_t toHash() {
+    if (getOwner() !is null)
+        return (cast(Object)getOwner()).toHash();
+    else
+        return super.toHash();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Clickable.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,508 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.Clickable;
+
+import dwt.dwthelper.utils;
+
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.Figure;
+import dwtx.draw2d.ActionListener;
+import dwtx.draw2d.ActionEvent;
+import dwtx.draw2d.ChangeListener;
+import dwtx.draw2d.ClickableEventHandler;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.ButtonModel;
+import dwtx.draw2d.ChangeEvent;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.ToggleModel;
+import dwtx.draw2d.StackLayout;
+import dwtx.draw2d.ColorConstants;
+import dwtx.draw2d.ButtonBorder;
+
+
+/**
+ * A Clickable responds to mouse clicks in some way (determined by a ClickBehavior) and
+ * fires action events. No visual appearance of feedback is offered. Depends on a model
+ * holder and an event handler which understands the model and updates the model
+ * accordingly. {@link ButtonModel} is used by default. Any figure can be set as contents
+ * to a Clickable. Clickable->EventHandler->Model->ModelObserver->Listeners of actions.
+ */
+public class Clickable
+    : Figure
+{
+
+private static const int
+    ROLLOVER_ENABLED_FLAG = Figure.MAX_FLAG << 1,
+    STYLE_BUTTON_FLAG = Figure.MAX_FLAG << 2,
+    STYLE_TOGGLE_FLAG = Figure.MAX_FLAG << 3;
+
+/**
+ * The highest reserved flag used by this class
+ */
+protected static int
+    MAX_FLAG = STYLE_TOGGLE_FLAG;
+
+/**
+ * Style constant that defines a push button. The button will be pressed when the mouse is
+ * pressed on top of it. The button will be released when the mouse is released or is move
+ * off of the button.
+ *
+ */
+public static const int STYLE_BUTTON = STYLE_BUTTON_FLAG;
+
+/**
+ * Style constant that defines a toggle button. The button will toggle between 2 states
+ * when the mouse is clicked on the button.
+ */
+public static const int STYLE_TOGGLE = STYLE_TOGGLE_FLAG;
+
+/**
+ * An action is performed every time the mouse is released.
+ */
+public static const int DEFAULT_FIRING = 0;
+
+/**
+ * Firing starts as soon as the mouse is pressed on this Clickable, and keeps firing at
+ * prefixed intervals until the mouse is released.
+ */
+public static const int REPEAT_FIRING  = 1;
+
+/**
+ * Observes the model for action and state changes.
+ *
+ * @see ActionListener
+ * @see ChangeListener
+ */
+/+static+/ interface ModelObserver
+    : ActionListener, ChangeListener
+{ }
+
+private ClickableEventHandler eventHandler;
+
+private ButtonModel model;
+
+private ModelObserver modelObserver;
+
+private void instanceInit(){
+    init();
+    setRequestFocusEnabled(true);
+    setFocusTraversable(true);
+}
+
+/**
+ * Constructs a Clickable with no contents.
+ */
+public this() {
+    instanceInit();
+}
+
+/**
+ * Constructs a Clickable whose contents are provided as input. The content figure
+ * occupies the entire region of the Clickable.
+ *
+ * @param contents The content figure
+ */
+public this(IFigure contents) {
+    this(contents, 0);
+}
+
+/**
+ * Constructs a Clickable whose contents are provided as input. The content figure
+ * occupies the entire region of the Clickable.  Sets the style to the given <i>style</i>
+ * (either {@link #STYLE_BUTTON} or {@link #STYLE_TOGGLE}).
+ *
+ * @param contents The content figure
+ * @param style The button style
+ */
+public this(IFigure contents, int style) {
+    instanceInit();
+    setContents(contents);
+    setStyle(style);
+}
+
+/**
+ * Adds the given listener to the list of action listeners of this Figure. Listener is
+ * called whenever an action is performed.
+ *
+ * @param listener The ActionListener to be added
+ * @since 2.0
+ */
+public void addActionListener(ActionListener listener) {
+    addListener(ActionListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * Adds the given listener to the list of state change listeners of this figure. A
+ * ChangeListener is informed if there is any state change in the model requiring action
+ * by the listener.
+ *
+ * @param listener The ChangeListener to be added
+ * @since 2.0
+ */
+public void addChangeListener(ChangeListener listener) {
+    addListener(ChangeListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * Returns a newly created ButtonModel as the default model to be used by this Clickable
+ * based on the button style.
+ *
+ * @return The model to be used by default
+ * @since 2.0
+ */
+protected ButtonModel createDefaultModel() {
+    if (isStyle(STYLE_TOGGLE))
+        return new ToggleModel();
+    else
+        return new ButtonModel();
+}
+
+/**
+ * Returns a newly created event handler for this Clickable and its model.
+ *
+ * @return The event handler
+ * @since 2.0
+ */
+protected ClickableEventHandler createEventHandler() {
+    return new ClickableEventHandler();
+}
+
+/**
+ * Returns a newly created model observer which listens to the model, and fires any action
+ * or state changes. A ModelObserver holds both an action listener and a state change
+ * listener.
+ *
+ * @return  The newly created model observer
+ * @since 2.0
+ */
+protected ModelObserver createModelObserver() {
+    return new class() ModelObserver {
+        public void actionPerformed(ActionEvent action) {
+            fireActionPerformed();
+        }
+        public void handleStateChanged(ChangeEvent change) {
+            fireStateChanged(change);
+        }
+    };
+}
+
+/**
+ * Fires an action performed event.
+ *
+ * @since 2.0
+ */
+public void doClick() {
+    fireActionPerformed();
+}
+
+/**
+ * Called when there has been an action performed by this Clickable, which is determined
+ * by the model. Notifies all ActionListener type listeners of an action performed.
+ *
+ * @since 2.0
+ */
+protected void fireActionPerformed() {
+    ActionEvent action = new ActionEvent(this);
+    Iterator listeners = getListeners(ActionListener.classinfo);
+    while (listeners.hasNext())
+        (cast(ActionListener)listeners.next()) //Leave newline for debug stepping
+            .actionPerformed(action);
+}
+
+/**
+ * Called when there has been a change of state in the model of this clickable. Notifies
+ * all ChangeListener type listeners  of the state change.
+ *
+ * @param modelChange The ChangeEvent
+ * @since 2.0
+ */
+protected void fireStateChanged(ChangeEvent modelChange) {
+    ChangeEvent change = new ChangeEvent(this, modelChange.getPropertyName());
+    Iterator listeners = getListeners(ChangeListener.classinfo);
+    while (listeners.hasNext())
+        (cast(ChangeListener)listeners.next())  //Leave newline for debug stepping
+            .handleStateChanged(change);
+}
+
+/**
+ * Returns the behavior model used by this Clickable.
+ *
+ * @return  The model used by this Clickable
+ * @since 2.0
+ */
+public ButtonModel getModel() {
+    return model;
+}
+
+/**
+ * Adds the given ClickableEventHandler to this clickable. A ClickableEventHandler
+ * should be a MouseListener, MouseMotionListener, ChangeListener, KeyListener,  and
+ * FocusListener.
+ *
+ * @param handler  The new event handler
+ * @since 2.0
+ */
+protected void hookEventHandler(ClickableEventHandler handler) {
+    if (handler is null)
+        return;
+    addMouseListener(handler);
+    addMouseMotionListener(handler);
+    addChangeListener(handler);
+    addKeyListener(handler);
+    addFocusListener(handler);
+}
+
+/**
+ * Initializes this Clickable by setting a default model and adding a clickable event
+ * handler for that model.
+ *
+ * @since 2.0
+ */
+protected void init() {
+    setModel(createDefaultModel());
+    setEventHandler(createEventHandler());
+}
+
+/**
+ * Returns <code>true</code> if rollover feedback is enabled.
+ *
+ * @return <code>true</code> rollover feedback is enabled
+ * @since 2.0
+ */
+public bool isRolloverEnabled() {
+    return (flags & ROLLOVER_ENABLED_FLAG) !is 0;
+}
+
+/**
+ * Returns <code>true</code> if this Clickable is in a selected state. The model is the
+ * one which holds all this state based information.
+ *
+ * @return <code>true</code> if this Clickable is in a selected state
+ * @since 2.0
+ */
+public bool isSelected() {
+    return getModel().isSelected();
+}
+
+/**
+ * Returns <code>true</code> if this Clickable's style is the same as the passed style.
+ *
+ * @param style The style to be checked
+ * @return <code>true</code> if this Clickable's style is the same as the passed style
+ * @since 2.0
+ */
+public bool isStyle(int style) {
+    return ((style & flags) is style);
+}
+
+/**
+ * If this Clickable has focus, this method paints a focus rectangle.
+ *
+ * @param graphics Graphics handle for painting
+ */
+protected void paintBorder(Graphics graphics) {
+    super.paintBorder(graphics);
+    if (hasFocus()) {
+        graphics.setForegroundColor(ColorConstants.black);
+        graphics.setBackgroundColor(ColorConstants.white);
+
+        Rectangle area = getClientArea();
+        if (isStyle(STYLE_BUTTON))
+            graphics.drawFocus(area.x, area.y, area.width, area.height);
+        else
+            graphics.drawFocus(area.x, area.y, area.width - 1, area.height - 1);
+    }
+}
+
+/**
+ * Paints the area of this figure excluded by the borders. Induces a (1,1) pixel shift in
+ * the painting if the  mouse is armed, giving it the pressed appearance.
+ *
+ * @param graphics  Graphics handle for painting
+ * @since 2.0
+ */
+protected void paintClientArea(Graphics graphics) {
+    if (isStyle(STYLE_BUTTON) && (getModel().isArmed() || getModel().isSelected())) {
+        graphics.translate(1, 1);
+        graphics.pushState();
+        super.paintClientArea(graphics);
+        graphics.popState();
+        graphics.translate(-1, -1);
+    } else
+        super.paintClientArea(graphics);
+}
+
+/**
+ * Removes the given listener from the list of ActionListener's of this Clickable.
+ *
+ * @param listener Listener to be removed from this figure
+ * @since 2.0
+ */
+public void removeActionListener(ActionListener listener) {
+    removeListener(ActionListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * Removes the given listener from the list of ChangeListener's of this clickable.
+ *
+ * @param listener Listener to be removed from this figure
+ * @since 2.0
+ */
+public void removeChangeListener(ChangeListener listener) {
+    removeListener(ChangeListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * Sets the Figure which is the contents of this Clickable. This Figure occupies the
+ * entire clickable region.
+ *
+ * @param contents Contents of the clickable
+ * @since 2.0
+ */
+protected void setContents(IFigure contents) {
+    setLayoutManager(new StackLayout());
+    if (getChildren().size() > 0)
+        remove(cast(IFigure)getChildren().get(0));
+    add(contents);
+}
+
+/**
+ * @see dwtx.draw2d.IFigure#setEnabled(bool)
+ */
+public void setEnabled(bool value) {
+    if (isEnabled() is value)
+        return;
+    super.setEnabled(value);
+    getModel().setEnabled(value);
+    setChildrenEnabled(value);
+}
+
+/**
+ * Sets the event handler which interacts with the model to determine the behavior of this
+ * Clickable.
+ *
+ * @param h  Event handler for this clickable
+ * @since 2.0
+ */
+public void setEventHandler(ClickableEventHandler h) {
+    if (eventHandler !is null)
+        unhookEventHandler(eventHandler);
+    eventHandler = h;
+    if (eventHandler !is null)
+        hookEventHandler(eventHandler);
+}
+
+/**
+ * Determines how this clickable is to fire notifications to its listeners. In the default
+ * firing method ({@link #DEFAULT_FIRING}), an action is performed  every time the mouse
+ * is released. In the repeat firing method ({@link #REPEAT_FIRING}), firing starts as
+ * soon as it is pressed on this clickable, and keeps firing at prefixed intervals until
+ * the mouse is released.
+ *
+ * @param type  Type of firing
+ * @since 2.0
+ */
+public void setFiringMethod(int type) {
+    getModel().setFiringBehavior(type);
+}
+
+/**
+ * Sets the model to be used by this clickable for its state and behavior determination.
+ * This clickable removes any observers from the previous model before adding new ones to
+ * the new model.
+ *
+ * @param model The new model of this Clickable
+ * @since 2.0
+ */
+public void setModel(ButtonModel model) {
+    if (this.model !is null) {
+        this.model.removeChangeListener(modelObserver);
+        this.model.removeActionListener(modelObserver);
+        modelObserver = null;
+    }
+    this.model = model;
+    if (model !is null) {
+        modelObserver = createModelObserver();
+        model.addActionListener(modelObserver);
+        model.addChangeListener(modelObserver);
+    }
+}
+
+/**
+ * Enables or disables rollover feedback of this figure. Generally used in conjunction
+ * with the model to determine if feedback is to be shown.
+ *
+ * @param value The rollover state to be set
+ * @since 2.0
+ */
+public void setRolloverEnabled(bool value) {
+    if (isRolloverEnabled() is value)
+        return;
+    setFlag(ROLLOVER_ENABLED_FLAG, value);
+    repaint();
+}
+
+/**
+ * Sets the selected state of this Clickable. Since the model is responsible for all state
+ * based information, it is informed of the state change. Extending classes can choose
+ * selection information, if they do not represent any selection.
+ *
+ * @param value  New selected state of this clickable.
+ * @see  #isSelected()
+ * @since 2.0
+ */
+public void setSelected(bool value) {
+    getModel().setSelected(value);
+}
+
+/**
+ * Sets this Clickable's style to the passed value, either {@link #STYLE_BUTTON} or
+ * {@link #STYLE_TOGGLE}.
+ *
+ * @param style The button style
+ * @since 2.0
+ */
+public void setStyle(int style) {
+    if ((style & STYLE_BUTTON) !is 0) {
+        setFlag(STYLE_BUTTON_FLAG, true);
+        if (!(null !is cast(ButtonBorder)getBorder() ))
+            setBorder(new ButtonBorder());
+        setOpaque(true);
+    } else {
+        setFlag(STYLE_BUTTON_FLAG, false);
+        setOpaque(false);
+    }
+
+    if ((style & STYLE_TOGGLE) !is 0) {
+        setFlag(STYLE_TOGGLE_FLAG, true);
+        setModel(createDefaultModel());
+    }
+}
+
+/**
+ * Removes the given ClickableEventHandler containing listeners from this Clickable.
+ *
+ * @param handler The event handler to be removed
+ * @since 2.0
+ */
+protected void unhookEventHandler(ClickableEventHandler handler) {
+    if (handler is null)
+        return;
+    removeMouseListener(handler);
+    removeMouseMotionListener(handler);
+    removeChangeListener(handler);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ClickableEventHandler.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ClickableEventHandler;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.MouseMotionListener;
+import dwtx.draw2d.MouseEvent;
+import dwtx.draw2d.KeyEvent;
+import dwtx.draw2d.FocusEvent;
+import dwtx.draw2d.KeyListener;
+import dwtx.draw2d.FocusListener;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.ChangeEvent;
+import dwtx.draw2d.ChangeListener;
+import dwtx.draw2d.MouseListener;
+import dwtx.draw2d.FigureListener;
+import dwtx.draw2d.Clickable;
+import dwtx.draw2d.ButtonModel;
+
+
+class ClickableEventHandler
+    : MouseMotionListener.Stub
+    ,
+        MouseListener,
+        FigureListener,
+        ChangeListener,
+        KeyListener,
+        FocusListener
+{
+
+private MouseEvent lastEvent;
+
+public void focusLost(FocusEvent fe) {
+    Clickable loser = cast(Clickable)fe.loser;
+    loser.repaint();
+    loser.getModel().setArmed(false);
+    loser.getModel().setPressed(false);
+}
+
+public void focusGained(FocusEvent fe) {
+    Clickable clickable = cast(Clickable)fe.gainer;
+    clickable.repaint();
+}
+
+public void figureMoved(IFigure source) {
+    if (lastEvent is null)
+        return;
+    mouseDragged(lastEvent);
+}
+
+public void handleStateChanged(ChangeEvent change) {
+    Clickable clickable = cast(Clickable)change.getSource();
+    if (change.getPropertyName() is ButtonModel.MOUSEOVER_PROPERTY
+        && !clickable.isRolloverEnabled())
+        return;
+    clickable.repaint();
+}
+
+public void mouseDoubleClicked(MouseEvent me) { }
+
+public void mouseDragged(MouseEvent me) {
+    lastEvent = me;
+    Clickable click = cast(Clickable)me.getSource();
+    ButtonModel model = click.getModel();
+    if (model.isPressed()) {
+        bool over = click.containsPoint(me.getLocation());
+            model.setArmed(over);
+            model.setMouseOver(over);
+    }
+}
+
+public void mouseEntered(MouseEvent me) {
+    Clickable click = cast(Clickable)me.getSource();
+    click.getModel().setMouseOver(true);
+    click.addFigureListener(this);
+}
+
+public void mouseExited(MouseEvent me) {
+    Clickable click = cast(Clickable)me.getSource();
+    click.getModel().setMouseOver(false);
+    click.removeFigureListener(this);
+}
+
+public void mouseMoved(MouseEvent me) { }
+
+public void mousePressed(MouseEvent me) {
+    if (me.button !is 1)
+        return;
+    lastEvent = me;
+    Clickable click = cast(Clickable)me.getSource();
+    ButtonModel model = click.getModel();
+    click.requestFocus();
+    model.setArmed(true);
+    model.setPressed(true);
+    me.consume();
+}
+
+public void mouseReleased(MouseEvent me) {
+    if (me.button !is 1)
+        return;
+    ButtonModel model = (cast(Clickable)me.getSource()).getModel();
+    if (!model.isPressed())
+        return;
+    model.setPressed(false);
+    model.setArmed(false);
+    me.consume();
+}
+
+public void keyPressed(KeyEvent ke) {
+    ButtonModel model = (cast(Clickable)ke.getSource()).getModel();
+    if (ke.character is ' ' || ke.character is '\r') {
+        model.setPressed(true);
+        model.setArmed(true);
+    }
+}
+
+public void keyReleased(KeyEvent ke) {
+    ButtonModel model = (cast(Clickable)ke.getSource()).getModel();
+    if (ke.character is ' ' || ke.character is '\r') {
+        model.setPressed(false);
+        model.setArmed(false);
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ColorConstants.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,196 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ColorConstants;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+import tango.core.sync.Mutex;
+
+import dwt.DWT;
+import dwt.graphics.Color;
+import dwt.widgets.Display;
+
+/**
+ * A collection of color-related constants.
+ */
+public struct ColorConstants {
+
+class SystemColorFactory {
+    private static Color getColor(int which) {
+        Display display = Display.getCurrent();
+        if (display !is null)
+            return display.getSystemColor(which);
+        display = Display.getDefault();
+        Color result;
+        scope Mutex mutex = new Mutex;
+        display.syncExec(dgRunnable( {
+            synchronized (mutex) {
+                result = Display.getCurrent().getSystemColor(which);
+            }
+        } ));
+        synchronized (mutex) {
+            return result;
+        }
+    }
+}
+
+/**
+ * @see DWT#COLOR_WIDGET_HIGHLIGHT_SHADOW
+ */
+static const Color buttonLightest;
+/**
+ * @see DWT#COLOR_WIDGET_BACKGROUND
+ */
+static const Color button;
+/**
+ * @see DWT#COLOR_WIDGET_NORMAL_SHADOW
+ */
+static const Color buttonDarker;
+/**
+ * @see DWT#COLOR_WIDGET_DARK_SHADOW
+ */
+static const Color buttonDarkest;
+
+/**
+ * @see DWT#COLOR_LIST_BACKGROUND
+ */
+static const Color listBackground;
+/**
+ * @see DWT#COLOR_LIST_FOREGROUND
+ */
+static const Color listForeground;
+
+/**
+ * @see DWT#COLOR_WIDGET_BACKGROUND
+ */
+static const Color menuBackground;
+/**
+ * @see DWT#COLOR_WIDGET_FOREGROUND
+ */
+static const Color menuForeground;
+/**
+ * @see DWT#COLOR_LIST_SELECTION
+ */
+static const Color menuBackgroundSelected;
+/**
+ * @see DWT#COLOR_LIST_SELECTION_TEXT
+ */
+static const Color menuForegroundSelected;
+
+/**
+ * @see DWT#COLOR_TITLE_BACKGROUND
+ */
+static const Color titleBackground;
+/**
+ * @see DWT#COLOR_TITLE_BACKGROUND_GRADIENT
+ */
+static const Color titleGradient;
+/**
+ * @see DWT#COLOR_TITLE_FOREGROUND
+ */
+static const Color titleForeground;
+/**
+ * @see DWT#COLOR_TITLE_INACTIVE_FOREGROUND
+ */
+static const Color titleInactiveForeground;
+/**
+ * @see DWT#COLOR_TITLE_INACTIVE_BACKGROUND
+ */
+static const Color titleInactiveBackground;
+/**
+ * @see DWT#COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT
+ */
+static const Color titleInactiveGradient;
+
+/**
+ * @see DWT#COLOR_INFO_FOREGROUND
+ */
+static const Color tooltipForeground;
+/**
+ * @see DWT#COLOR_INFO_BACKGROUND
+ */
+static const Color tooltipBackground;
+
+/*
+ * Misc. colors
+ */
+/** One of the pre-defined colors */
+static const Color white;
+/** One of the pre-defined colors */
+static const Color lightGray;
+/** One of the pre-defined colors */
+static const Color gray;
+/** One of the pre-defined colors */
+static const Color darkGray;
+/** One of the pre-defined colors */
+static const Color black;
+/** One of the pre-defined colors */
+static const Color red;
+/** One of the pre-defined colors */
+static const Color orange;
+/** One of the pre-defined colors */
+static const Color yellow;
+/** One of the pre-defined colors */
+static const Color green;
+/** One of the pre-defined colors */
+static const Color lightGreen;
+/** One of the pre-defined colors */
+static const Color darkGreen;
+/** One of the pre-defined colors */
+static const Color cyan;
+/** One of the pre-defined colors */
+static const Color lightBlue;
+/** One of the pre-defined colors */
+static const Color blue;
+/** One of the pre-defined colors */
+static const Color darkBlue;
+
+
+static this(){
+    buttonLightest          = SystemColorFactory.getColor(DWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
+    button                  = SystemColorFactory.getColor(DWT.COLOR_WIDGET_BACKGROUND);
+    buttonDarker            = SystemColorFactory.getColor(DWT.COLOR_WIDGET_NORMAL_SHADOW);
+    buttonDarkest           = SystemColorFactory.getColor(DWT.COLOR_WIDGET_DARK_SHADOW);
+    listBackground          = SystemColorFactory.getColor(DWT.COLOR_LIST_BACKGROUND);
+    listForeground          = SystemColorFactory.getColor(DWT.COLOR_LIST_FOREGROUND);
+    menuBackground          = SystemColorFactory.getColor(DWT.COLOR_WIDGET_BACKGROUND);
+    menuForeground          = SystemColorFactory.getColor(DWT.COLOR_WIDGET_FOREGROUND);
+    menuBackgroundSelected  = SystemColorFactory.getColor(DWT.COLOR_LIST_SELECTION);
+    menuForegroundSelected  = SystemColorFactory.getColor(DWT.COLOR_LIST_SELECTION_TEXT);
+    titleBackground         = SystemColorFactory.getColor(DWT.COLOR_TITLE_BACKGROUND);
+    titleGradient           = SystemColorFactory.getColor(DWT.COLOR_TITLE_BACKGROUND_GRADIENT);
+    titleForeground         = SystemColorFactory.getColor(DWT.COLOR_TITLE_FOREGROUND);
+    titleInactiveForeground = SystemColorFactory.getColor(DWT.COLOR_TITLE_INACTIVE_FOREGROUND);
+    titleInactiveBackground = SystemColorFactory.getColor(DWT.COLOR_TITLE_INACTIVE_BACKGROUND);
+    titleInactiveGradient   = SystemColorFactory.getColor(DWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT);
+    tooltipForeground       = SystemColorFactory.getColor(DWT.COLOR_INFO_FOREGROUND);
+    tooltipBackground       = SystemColorFactory.getColor(DWT.COLOR_INFO_BACKGROUND);
+    white      = new Color(null, 255, 255, 255);
+    lightGray  = new Color(null, 192, 192, 192);
+    gray       = new Color(null, 128, 128, 128);
+    darkGray   = new Color(null,  64,  64,  64);
+    black      = new Color(null,   0,   0,   0);
+    red        = new Color(null, 255,   0,   0);
+    orange     = new Color(null, 255, 196,   0);
+    yellow     = new Color(null, 255, 255,   0);
+    green      = new Color(null,   0, 255,   0);
+    lightGreen = new Color(null,  96, 255,  96);
+    darkGreen  = new Color(null,   0, 127,   0);
+    cyan       = new Color(null,   0, 255, 255);
+    lightBlue  = new Color(null, 127, 127, 255);
+    blue       = new Color(null,   0,   0, 255);
+    darkBlue   = new Color(null,   0,   0, 127);
+}
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/CompoundBorder.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.CompoundBorder;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+
+import dwtx.draw2d.AbstractBorder;
+import dwtx.draw2d.Border;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Graphics;
+
+/**
+ * CompoundBorder allows for the nesting of two borders. The nested borders are referred
+ * to as the <i>inner</i> and <i>outer</i> borders.
+ */
+public class CompoundBorder
+    : AbstractBorder
+{
+
+/** The inner Border. */
+protected Border inner;
+/** The outer Border. */
+protected Border outer;
+
+/**
+ * Constructs a default CompoundBorder with no borders under it.
+ *
+ * @since 2.0
+ */
+public this() { }
+
+/**
+ * Constructs a CompoundBorder with the two borders specified as input.
+ *
+ * @param outer  Border which is drawn on the outside
+ * @param inner  Border which is drawn inside the outer border
+ *
+ * @since 2.0
+ */
+public this(Border outer, Border inner) {
+    this.outer = outer;
+    this.inner = inner;
+}
+
+/**
+ * Returns the inner border of this CompoundBorder.
+ *
+ * @return The inner border
+ * @since 2.0
+ */
+public Border getInnerBorder() {
+    return inner;
+}
+
+/**
+ * Returns the total insets required to hold both the inner and outer borders of this
+ * CompoundBorder.
+ *
+ * @param figure  Figure for which this is the border
+ * @return  The total insets for this border
+ * @since 2.0
+ */
+public Insets getInsets(IFigure figure) {
+    Insets insets = null;
+    if (inner !is null)
+        insets = inner.getInsets(figure);
+    else
+        insets = new Insets();
+    if (outer !is null) {
+        Insets moreInsets = outer.getInsets(figure);
+        insets = insets.getAdded(moreInsets);
+    }
+    return insets;
+}
+
+/**
+ * @see dwtx.draw2d.Border#getPreferredSize(IFigure)
+ */
+public Dimension getPreferredSize(IFigure fig) {
+    Dimension prefSize = new Dimension(inner.getPreferredSize(fig));
+    Insets outerInsets = outer.getInsets(fig);
+    prefSize.expand(outerInsets.getWidth(), outerInsets.getHeight());
+    prefSize.union_(outer.getPreferredSize(fig));
+    return prefSize;
+}
+
+/**
+ * Returns the outer border of this CompoundBorder.
+ *
+ * @return The outer border
+ * @since 2.0
+ */
+public Border getOuterBorder() {
+    return outer;
+}
+
+/**
+ * Returns <code>true</code> if this border is opaque. Return value is dependent on the
+ * opaque state of both the borders it contains. Both borders have to be opaque for this
+ * border to be opaque. In the absence of any of the borders, this border is not opaque.
+ *
+ * @return <code>true</code> if this border is opaque
+ */
+public bool isOpaque() {
+    return ((inner !is null) ? inner.isOpaque() : false)
+            && ((outer !is null) ? outer.isOpaque() : false);
+}
+
+/**
+ * @see dwtx.draw2d.Border#paint(IFigure, Graphics, Insets)
+ */
+public void paint(IFigure figure, Graphics g, Insets insets) {
+    if (outer !is null) {
+        g.pushState();
+        outer.paint(figure, g, insets);
+        g.popState();
+
+        insets = insets.getAdded(outer.getInsets(figure));
+    }
+    if (inner !is null)
+        inner.paint(figure, g, insets);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Connection.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.Connection;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.ConnectionRouter;
+import dwtx.draw2d.ConnectionAnchor;
+
+/**
+ * A Connection is a figure that connects two objects.
+ */
+public interface Connection : IFigure {
+
+/**
+ * The connection router property.  Used to signify that the ConnectionRouter has changed.
+ */
+static const String PROPERTY_CONNECTION_ROUTER = "connectionRouter"; //$NON-NLS-1$
+/**
+ * The points property.  Used to signify the points in the Connection have changed.
+ */
+static const String PROPERTY_POINTS = "points"; //$NON-NLS-1$
+
+/**
+ * Returns the ConnectionRouter used to route this Connection.  Does not return null.
+ *
+ * @return The ConnectionRouter for this Connection
+ */
+ConnectionRouter getConnectionRouter();
+
+/**
+ * Sets the ConnectionRouter for this Connection.
+ *
+ * @param router The ConnectionRouter to set for this Connection
+ */
+void setConnectionRouter(ConnectionRouter router);
+
+/**
+ * Returns the ConnectionAnchor at the <b>source</b> end of this Connection.
+ * @return The ConnectionAnchor at the <b>source</b> end of this Connection
+ */
+ConnectionAnchor getSourceAnchor();
+
+/**
+ * Returns the ConnectionAnchor at the <b>target</b> end of this Connection.
+ * @return The ConnectionAnchor at the <b>target</b> end of this Connection
+ */
+ConnectionAnchor getTargetAnchor();
+
+/**
+ * Returns the routing constraint.  May be <code>null</code>.
+ * @return The routing constraint
+ */
+Object getRoutingConstraint();
+
+/**
+ * Sets the routing constraint used by the router.
+ * @param cons The routing constraint
+ */
+void setRoutingConstraint(Object cons);
+
+/**
+ * Sets the ConnectionAnchor to be used at the <i>source</i> end of this Connection.
+ * @param anchor The source anchor
+ */
+void setSourceAnchor(ConnectionAnchor anchor);
+
+/**
+ * Sets the ConnectionAnchor to be used at the <i>target</i> end of this Connection.
+ * @param anchor The target anchor
+ */
+void setTargetAnchor(ConnectionAnchor anchor);
+
+/**
+ * Returns the PointList containing the Points that make up this Connection.  This may be
+ * returned by reference.
+ * @return The points for this Connection
+ */
+PointList getPoints();
+
+/**
+ * Sets the PointList containing the Points that make up this Connection.
+ * @param list The points for this Connection
+ */
+void setPoints(PointList list);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ConnectionAnchor.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ConnectionAnchor;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.AnchorListener;
+import dwtx.draw2d.IFigure;
+
+/**
+ * An object to which a {@link Connection} will be anchored.  If the ConnectionAnchor
+ * moves, the Connection should move with it.
+ */
+public interface ConnectionAnchor {
+
+/**
+ * Adds a listener interested in the movement of this ConnectionAnchor.
+ * @param listener The AnchorListener to be added
+ */
+void addAnchorListener(AnchorListener listener);
+
+/**
+ * Returns the location where the Connection should be anchored in absolute coordinates.
+ * The anchor may use the given reference Point to calculate this location.
+ * @param reference The reference Point in absolute coordinates
+ * @return The anchor's location
+ */
+Point getLocation(Point reference);
+
+/**
+ * Returns the IFigure that contains this ConnectionAnchor.  Moving this figure will cause
+ * the anchor to move with it.
+ * @return The IFigure that contains this ConnectionAnchor
+ */
+IFigure getOwner();
+
+/**
+ * Returns the reference point for this anchor in absolute coordinates. This might be used
+ * by another anchor to determine its own location (i.e. {@link ChopboxAnchor}).
+ * @return The reference Point
+ */
+Point getReferencePoint();
+
+/**
+ * Removes the listener.
+ * @param listener The AnchorListener to be removed
+ */
+void removeAnchorListener(AnchorListener listener);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ConnectionAnchorBase.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ConnectionAnchorBase;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.ConnectionAnchor;
+import dwtx.draw2d.AnchorListener;
+
+/**
+ * Provides support for a ConnectionAnchor. A ConnectionAnchor is one of the end points
+ * of a {@link Connection}. It holds listeners and notifies them if the anchor is moved.
+ */
+public abstract class ConnectionAnchorBase
+    : ConnectionAnchor
+{
+
+/**
+ * The list of listeners
+ */
+protected List listeners;
+
+this(){
+    listeners = new ArrayList(1);
+}
+
+/**
+ * @see dwtx.draw2d.ConnectionAnchor#addAnchorListener(AnchorListener)
+ */
+public void addAnchorListener(AnchorListener listener) {
+    listeners.add(cast(Object)listener);
+}
+
+/**
+ * @see dwtx.draw2d.ConnectionAnchor#removeAnchorListener(AnchorListener)
+ */
+public void removeAnchorListener(AnchorListener listener) {
+    listeners.remove(cast(Object)listener);
+}
+
+/**
+ * Notifies all the listeners in the list of a change in position of this anchor. This is
+ * called from one of the implementing anchors when its location is changed.
+ *
+ * @since 2.0
+ */
+protected void fireAnchorMoved() {
+    Iterator iter = listeners.iterator();
+    while (iter.hasNext())
+        (cast(AnchorListener)iter.next()).anchorMoved(this);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ConnectionEndpointLocator.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,282 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ConnectionEndpointLocator;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.geometry.Transposer;
+import dwtx.draw2d.Locator;
+import dwtx.draw2d.Connection;
+import dwtx.draw2d.IFigure;
+
+/**
+ * Used to place IFigures along the endpoint or starting point of a {@link Connection}.
+ * <code>uDistance</code> represents the distance from the Connection's owner to the
+ * IFigure. <code>vDistance</code> represents the distance from the IFigure to the
+ * Connection itself.
+ */
+public class ConnectionEndpointLocator
+    : Locator
+{
+
+private bool end;
+private Connection conn;
+private int uDistance;
+private int vDistance;
+private static Rectangle figureBounds;
+
+/**
+ * Transposes the location if the connection point is along the top or bottom of its owner
+ * figure.
+ */
+protected Transposer transposer;
+
+/**
+ * Constructs a ConnectionEndpointLocator using the given {@link Connection}.  If
+ * <i>isEnd</i> is <code>true</code>, the location is relative to the Connection's end (or
+ * target) point. If <i>isEnd</i> is <code>false</code>, the location is relative to the
+ * Connection's start (or source) point.
+ *
+ * @param c The Connection
+ * @param isEnd <code>true</code> is location is relative to end point
+ * @since 2.0
+ */
+public this(Connection c, bool isEnd) {
+    transposer = new Transposer();
+    end = isEnd;
+    conn = c;
+    uDistance = 14;
+    vDistance = 4;
+    figureBounds = new Rectangle();
+}
+
+/*
+ * Returns an integer representing the side of the passed Rectangle that a point lies on.
+ *  1 is Top
+ *  2 is Right
+ *  3 is Bottom
+ *  4 is Left
+ *
+ * @param loc The point that is to be located
+ */
+private int calculateConnectionLocation(Point loc, Point topLeft, Point center) {
+    double m1, m2 = 0;
+    m1 = cast(double)(topLeft.y - center.y) / cast(double)(topLeft.x - center.x);
+
+    if (loc.x - center.x !is 0)
+        m2 = cast(double)(loc.y - center.y) / cast(double)(loc.x - center.x);
+
+    if (loc.x is center.x) {
+        // Case where m2 is vertical
+        if (loc.y < center.y)
+            return 3;
+        else
+            return 1;
+    } else if (Math.abs(m2) <= Math.abs(m1)) {
+        // Connection start point along left or right side
+        if (loc.x < center.x)
+            return 4;
+        else
+            return 2;
+    } else {
+        // Connection start point along top or bottom
+        if (loc.y < center.y)
+            return 3;
+        else
+            return 1;
+    }
+}
+
+/*
+ * This method is used to calculate the "quadrant" value of a connection that does not
+ * have an owner on its starting point.
+ *
+ * 1 is Top
+ * 2 is Right
+ * 3 is Bottom
+ * 4 is Left
+ *
+ * @param startPoint The starting point of the connection.
+ * @param endPoint The end point of the connection.
+ */
+private int calculateConnectionLocation(Point startPoint, Point endPoint) {
+    if (Math.abs(endPoint.x - startPoint.x) > Math.abs(endPoint.y - startPoint.y)) {
+        if (endPoint.x > startPoint.x)
+            return 2;
+        else
+            return 4;
+    } else {
+        if (endPoint.y > startPoint.y)
+            return 1;
+        else
+            return 3;
+    }
+}
+
+/*
+ * Calculates 'tan' which is used as a factor for y adjustment when placing the connection
+ * label. 'tan' is capped at 1.0 in the positive direction and -1.0 in the negative
+ * direction.
+ *
+ * @param startPoint The starting point of the connection.
+ * @param endPoint The end point of the connection.
+ * @since 2.0
+ */
+private double calculateTan(Point startPoint, Point endPoint) {
+    double tan = 0;
+    if (endPoint.x is startPoint.x)
+        tan = 1.0;
+    else
+        tan = cast(double)(endPoint.y - startPoint.y)
+                / cast(double)(endPoint.x - startPoint.x);
+    if (tan > 1)
+        tan = 1.0;
+    else if (tan < -1)
+        tan = -1.0;
+
+    return tan;
+}
+
+private int calculateYShift(int figureWidth, int figureHeight) {
+    int yShift = 0;
+    if (vDistance < 0)
+        yShift = -figureHeight;
+    else if (vDistance is 0)
+        yShift = -figureHeight / 2;
+    return yShift;
+}
+
+private Connection getConnection() {
+    return conn;
+}
+
+private IFigure getConnectionOwner() {
+    IFigure connOwner;
+    if (isEnd())
+        connOwner = conn.getTargetAnchor().getOwner();
+    else
+        connOwner = conn.getSourceAnchor().getOwner();
+
+    return connOwner;
+}
+
+/**
+ * Returns the distance in pixels from the anchor's owner.
+ * @return the offset distance from the endpoint figure
+ */
+public int getUDistance() {
+    return uDistance;
+}
+
+/**
+ * Returns the distance in pixels from the connection
+ * @return the offset from the connection itself
+ */
+public int getVDistance() {
+    return vDistance;
+}
+
+private bool isEnd() {
+    return end;
+}
+
+/**
+ * Relocates the given IFigure at either the source or target end of the Connection,
+ * based on the <code>bool</code> given in the constructor
+ * {@link #ConnectionEndpointLocator(Connection, bool)}.
+ *
+ * @param figure The figure to relocate
+ */
+public void relocate(IFigure figure) {
+    Connection conn = getConnection();
+    Point startPoint = Point.SINGLETON;
+    Point endPoint = new Point();
+
+    int startPointPosition = 0;
+    int endPointPosition = 1;
+    if (isEnd()) {
+        startPointPosition = conn.getPoints().size() - 1;
+        endPointPosition = startPointPosition - 1;
+    }
+
+    conn.getPoints().getPoint(startPoint, startPointPosition);
+    conn.getPoints().getPoint(endPoint, endPointPosition);
+
+    IFigure connOwner = getConnectionOwner();
+
+    int quadrant;
+    if (connOwner !is null) {
+        Rectangle connOwnerBounds = connOwner.getBounds();
+        Point connOwnerCenter = connOwnerBounds.getCenter();
+        Point connOwnerTL = connOwnerBounds.getTopLeft();
+        quadrant = calculateConnectionLocation(startPoint, connOwnerTL, connOwnerCenter);
+    } else
+        quadrant = calculateConnectionLocation(startPoint, endPoint);
+
+    int cos = 1;
+    transposer.setEnabled(false);
+
+    /*
+     * Label placement calculations are done as if the connection point is along the left
+     * or right side of the  figure. If the connection point is along the top or bottom,
+     * values are transposed.
+     */
+    if (quadrant is 1 || quadrant is 3)
+        transposer.setEnabled(true);
+
+    if (quadrant is 3 || quadrant is 4)
+        cos = -1;
+
+    Dimension figureSize = transposer.t(figure.getPreferredSize());
+    startPoint = transposer.t(startPoint);
+    endPoint = transposer.t(endPoint);
+
+    double tan = calculateTan(startPoint, endPoint);
+
+    int figureWidth = figureSize.width;
+    int figureHeight = figureSize.height;
+    int yShift = calculateYShift(figureWidth, figureHeight);
+
+    Point figurePoint =
+        new Point(startPoint.x + (uDistance * cos) + figureWidth * ((cos - 1) / 2),
+                cast(int)(startPoint.y + cos * uDistance * tan + vDistance + yShift));
+
+    figureBounds.setSize(transposer.t(figureSize));
+    figureBounds.setLocation(transposer.t(figurePoint));
+    figure.setBounds(figureBounds);
+}
+
+/**
+ * Sets the distance in pixels from the Connection's owner.
+ *
+ * @param distance Number of pixels to place the ConnectionEndpointLocator from its owner.
+ * @since 2.0
+ */
+public void setUDistance(int distance) {
+    uDistance = distance;
+}
+
+/**
+ * Sets the distance in pixels from the Connection.
+ *
+ * @param distance Number of pixels to place the ConnectionEndpointLocator from its
+ * Connection.
+ * @since 2.0
+ */
+public void setVDistance(int distance) {
+    vDistance = distance;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ConnectionLayer.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ConnectionLayer;
+
+import dwt.dwthelper.utils;
+
+import dwt.DWT;
+import dwtx.draw2d.FreeformLayer;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.ConnectionRouter;
+import dwtx.draw2d.Connection;
+
+/**
+ * Layer designed specifically to handle the presence of connections. This is done due to
+ * the necessity of having a router for the connections added.
+ */
+public class ConnectionLayer
+    : FreeformLayer
+{
+
+int antialias = DWT.DEFAULT;
+
+/**
+ * The ConnectionRouter used to route all connections on this layer.
+ */
+protected ConnectionRouter connectionRouter;
+
+/**
+ * Adds the given figure with the given contraint at the given index. If the figure is a
+ * {@link Connection}, its {@link ConnectionRouter} is set.
+ *
+ * @param figure  Figure being added
+ * @param constraint  Constraint of the figure being added
+ * @param index  Index where the figure is to be added
+ * @since 2.0
+ */
+public void add(IFigure figure, Object constraint, int index) {
+    super.add(figure, constraint, index);
+
+    // If the connection layout manager is set, then every
+    // figure added should use this layout manager.
+    if (null !is cast(Connection )figure && getConnectionRouter() !is null)
+        (cast(Connection)figure).setConnectionRouter(getConnectionRouter());
+}
+
+/**
+ * Returns the ConnectionRouter being used by this layer.
+ *
+ * @return  ConnectionRouter being used by this layer
+ * @since 2.0
+ */
+public ConnectionRouter getConnectionRouter() {
+    return connectionRouter;
+}
+
+/**
+ * @see IFigure#paint(Graphics)
+ */
+public void paint(Graphics graphics) {
+    if (antialias !is DWT.DEFAULT)
+        graphics.setAntialias(antialias);
+    super.paint(graphics);
+}
+
+/**
+ * Removes the figure from this Layer.  If the figure is a {@link Connection}, that
+ * Connection's {@link ConnectionRouter} is set to <code>null</code>.
+ *
+ * @param figure The figure to remove
+ */
+public void remove(IFigure figure) {
+    if ( auto f = cast(Connection)figure )
+        f.setConnectionRouter(null);
+    super.remove(figure);
+}
+
+/**
+ * Sets the ConnectionRouter for this layer. This router is set as the ConnectionRouter
+ * for all the child connections of this Layer.
+ *
+ * @param router  The ConnectionRouter to set for this Layer
+ * @since 2.0
+ */
+public void setConnectionRouter(ConnectionRouter router) {
+    connectionRouter = router;
+    FigureIterator iter = new FigureIterator(this);
+    IFigure figure;
+    while (iter.hasNext()) {
+        figure = iter.nextFigure();
+        if ( auto f = cast(Connection)figure )
+            f.setConnectionRouter(router);
+    }
+}
+
+/**
+ * Sets whether antialiasing should be enabled for the connection layer. If this value is
+ * set to something other than {@link DWT#DEFAULT}, {@link Graphics#setAntialias(int)}
+ * will be called with the given value when painting this layer.
+ * @param antialias the antialias setting
+ * @since 3.1
+ */
+public void setAntialias(int antialias) {
+    this.antialias = antialias;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ConnectionLocator.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,165 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ConnectionLocator;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.AbstractLocator;
+import dwtx.draw2d.Connection;
+
+/**
+ * Repositions a {@link Figure} attached to a {@link Connection} when the Connection is
+ * moved.  Provides for alignment at the start (source), middle, or end (target) of the
+ * Connection.
+ */
+public class ConnectionLocator
+    : AbstractLocator
+{
+
+/** @deprecated Use {@link #SOURCE} */
+public static const int START  = 2;
+/** The start (or source) of the Connection */
+public static const int SOURCE = 2;
+
+/** @deprecated Use {@link #TARGET} */
+public static const int END    = 3;
+/** The end (or target) of the Connection */
+public static const int TARGET = 3;
+
+/**
+ * @deprecated Use {@link #MIDDLE} instead, since the location is not the midpoint of a
+ * line-segment, but the middle of a polyline.
+ */
+public static const int MIDPOINT   = 4;
+/** The middle of the Connection */
+public static const int MIDDLE     = 4;
+
+private Connection connection;
+private int alignment;
+
+/**
+ * Constructs a ConnectionLocator with the passed connection and {@link #MIDDLE}
+ * alignment.
+ *
+ * @param connection The Connection
+ * @since 2.0
+ */
+public this(Connection connection) {
+    this(connection, MIDDLE);
+}
+
+/**
+ * Constructs a ConnectionLocator with the passed Connection and alignment. Valid values
+ * for the alignment are integer constants {@link #SOURCE}, {@link #MIDDLE}, and
+ * {@link #TARGET}.
+ *
+ * @param connection The Connection
+ * @param align The alignment
+ *
+ * @since 2.0
+ */
+public this(Connection connection, int align_) {
+    setConnection(connection);
+    setAlignment(align_);
+}
+
+/**
+ * Returns the alignment of ConnectionLocator.
+ *
+ * @return The alignment
+ * @since 2.0
+ */
+public int getAlignment() {
+    return alignment;
+}
+
+/**
+ * Returns connection associated with ConnectionLocator.
+ *
+ * @return The Connection
+ * @since 2.0
+ */
+protected Connection getConnection() {
+    return connection;
+}
+
+/**
+ * Returns ConnectionLocator's reference point in absolute coordinates.
+ *
+ * @return The reference point
+ * @since 2.0
+ */
+protected Point getReferencePoint() {
+    Point p = getLocation(getConnection().getPoints());
+    getConnection().translateToAbsolute(p);
+    return p;
+}
+
+/**
+ * Returns a point from the passed PointList, dependent on ConnectionLocator's alignment.
+ * If the alignment is {@link #SOURCE}, it returns the first point in <i>points</i>. If
+ * {@link #TARGET}, it returns the last point in <i>points</i>. If {@link #MIDDLE}, it
+ * returns the middle of line represented by <i>points</i>.
+ *
+ * @param points The points in the Connection
+ * @return The location
+ * @since 2.0
+ */
+protected Point getLocation(PointList points) {
+    switch (getAlignment()) {
+        case SOURCE:
+            return points.getPoint(Point.SINGLETON, 0);
+        case TARGET:
+            return points.getPoint(Point.SINGLETON, points.size() - 1);
+        case MIDDLE:
+            if (points.size() % 2 is 0) {
+                int i = points.size() / 2;
+                int j = i - 1;
+                Point p1 = points.getPoint(j);
+                Point p2 = points.getPoint(i);
+                Dimension d = p2.getDifference(p1);
+                return Point.SINGLETON.setLocation(p1.x + d.width / 2,
+                                                    p1.y + d.height / 2);
+            }
+            int i = (points.size() - 1) / 2;
+            return points.getPoint(Point.SINGLETON, i);
+        default:
+            return new Point();
+    }
+}
+
+/**
+ * Sets the alignment.  Possible values are {@link #SOURCE}, {@link #MIDDLE}, and
+ * {@link #TARGET}.
+ *
+ * @param align The alignment
+ * @since 2.0
+ */
+protected void setAlignment(int align_) {
+    alignment = align_;
+}
+
+/**
+ * Sets the Connection to be associated with this ConnectionLocator.
+ *
+ * @param connection The Connection
+ * @since 2.0
+ */
+protected void setConnection(Connection connection) {
+    this.connection = connection;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ConnectionRouter.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ConnectionRouter;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.AnchorListener;
+import dwtx.draw2d.AbstractRouter;
+import dwtx.draw2d.Connection;
+
+/**
+ * Routes a {@link Connection}, possibly using a constraint.
+ */
+public interface ConnectionRouter {
+
+/**
+ * Returns the constraint for the Connection.
+ * @param connection The connection
+ * @return The constraint
+ */
+Object getConstraint(Connection connection);
+
+/**
+ * Invalidates the given Connection.
+ * @param connection The connection to be invalidated
+ */
+void invalidate(Connection connection);
+
+/**
+ * Routes the Connection.
+ * @param connection The Connection to route
+ */
+void route(Connection connection);
+
+/**
+ * Removes the Connection from this router.
+ * @param connection The Connection to remove
+ */
+void remove(Connection connection);
+
+/**
+ * Maps the given constraint to the given Connection.
+ * @param connection The Connection
+ * @param constraint The constraint
+ */
+void setConstraint(Connection connection, Object constraint);
+
+
+}
+/**
+ * Routes Connections directly from the source anchor to the target anchor with no
+ * bendpoints in between.
+ */
+class NullConnectionRouter
+    : AbstractRouter
+{
+
+    /**
+     * Constructs a new NullConnectionRouter.
+     */
+    this() { }
+
+    /**
+     * Routes the given Connection directly between the source and target anchors.
+     * @param conn the connection to be routed
+     */
+    public void route(Connection conn) {
+        PointList points = conn.getPoints();
+        points.removeAllPoints();
+        Point p;
+        conn.translateToRelative(p = getStartPoint(conn));
+        points.addPoint(p);
+        conn.translateToRelative(p = getEndPoint(conn));
+        points.addPoint(p);
+        conn.setPoints(points);
+    }
+}
+
+/**
+ * The default router for Connections.
+ */
+public static const ConnectionRouter ConnectionRouter_NULL;
+
+static this(){
+    ConnectionRouter_NULL = new NullConnectionRouter();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/CoordinateListener.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.CoordinateListener;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.IFigure;
+
+/**
+ * @since 3.1
+ */
+public interface CoordinateListener {
+
+/**
+ * Indicates that the coordinate system has changed in a way that affects the absolute
+ * locations of contained figures.
+ * @param source the figure whose coordinate system changed
+ * @since 3.1
+ */
+void coordinateSystemChanged(IFigure source);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Cursors.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,199 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.Cursors;
+
+import dwt.dwthelper.utils;
+
+import dwt.DWT;
+import dwt.graphics.Cursor;
+import dwtx.draw2d.PositionConstants;
+
+/**
+ * A collection of cursors.
+ */
+public class Cursors {
+
+/**
+ * Returns the cursor corresponding to the given direction, defined in
+ * {@link PositionConstants}. Note that {@link #getDirectionalCursor(int, bool)} should
+ * be used for applications which want to run properly when running in a mirrored
+ * environment. The behavior is the same as calling {@link #getDirectionalCursor(int,
+ * bool) getDirectionalCursor(direction, false)}.
+ *
+ * @param direction the relative direction of the desired cursor
+ * @return The appropriate directional cursor
+ */
+public static Cursor getDirectionalCursor(int direction) {
+    return getDirectionalCursor(direction, false);
+}
+
+/**
+ * Returns the cursor corresponding to the given direction and mirroring. The direction
+ * must be one of:
+ * <UL>
+ *   <LI>{@link PositionConstants#NORTH}
+ *   <LI>{@link PositionConstants#SOUTH}
+ *   <LI>{@link PositionConstants#EAST}
+ *   <LI>{@link PositionConstants#WEST}
+ *   <LI>{@link PositionConstants#NORTH_EAST}
+ *   <LI>{@link PositionConstants#NORTH_WEST}
+ *   <LI>{@link PositionConstants#SOUTH_EAST}
+ *   <LI>{@link PositionConstants#SOUTH_WEST}
+ * </UL>
+ * <P>The behavior is undefined for other values. If <code>isMirrored</code> is set to
+ * <code>true</code>, EAST and WEST will be inverted.
+ * @param direction the relative direction of the desired cursor
+ * @param isMirrored <code>true</code> if EAST and WEST should be inverted
+ * @return The appropriate directional cursor
+ */
+public static Cursor getDirectionalCursor(int direction, bool isMirrored) {
+    if (isMirrored && (direction & PositionConstants.EAST_WEST) !is 0)
+        direction = direction ^ PositionConstants.EAST_WEST;
+    switch (direction) {
+        case PositionConstants.NORTH :
+            return SIZEN;
+        case PositionConstants.SOUTH:
+            return SIZES;
+        case PositionConstants.EAST :
+            return SIZEE;
+        case PositionConstants.WEST:
+            return SIZEW;
+        case PositionConstants.SOUTH_EAST:
+            return SIZESE;
+        case PositionConstants.SOUTH_WEST:
+            return SIZESW;
+        case PositionConstants.NORTH_EAST:
+            return SIZENE;
+        case PositionConstants.NORTH_WEST:
+            return SIZENW;
+        default:
+            break;
+    }
+    return null;
+}
+
+/**
+ * @see DWT#CURSOR_ARROW
+ */
+public static const Cursor ARROW;
+/**
+ * @see DWT#CURSOR_SIZEN
+ */
+public static const Cursor SIZEN;
+/**
+ * @see DWT#CURSOR_SIZENE
+ */
+public static const Cursor SIZENE;
+/**
+ * @see DWT#CURSOR_SIZEE
+ */
+public static const Cursor SIZEE;
+/**
+ * @see DWT#CURSOR_SIZESE
+ */
+public static const Cursor SIZESE;
+/**
+ * @see DWT#CURSOR_SIZES
+ */
+public static const Cursor SIZES;
+/**
+ * @see DWT#CURSOR_SIZESW
+ */
+public static const Cursor SIZESW;
+/**
+ * @see DWT#CURSOR_SIZEW
+ */
+public static const Cursor SIZEW;
+/**
+ * @see DWT#CURSOR_SIZENW
+ */
+public static const Cursor SIZENW;
+/**
+ * @see DWT#CURSOR_APPSTARTING
+ */
+public static const Cursor APPSTARTING;
+/**
+ * @see DWT#CURSOR_CROSS
+ */
+public static const Cursor CROSS;
+/**
+ * @see DWT#CURSOR_HAND
+ */
+public static const Cursor HAND;
+/**
+ * @see DWT#CURSOR_HELP
+ */
+public static const Cursor HELP;
+/**
+ * @see DWT#CURSOR_IBEAM
+ */
+public static const Cursor IBEAM;
+/**
+ * @see DWT#CURSOR_NO
+ */
+public static const Cursor NO;
+/**
+ * @see DWT#CURSOR_SIZEALL
+ */
+public static const Cursor SIZEALL;
+/**
+ * @see DWT#CURSOR_SIZENESW
+ */
+public static const Cursor SIZENESW;
+/**
+ * @see DWT#CURSOR_SIZENWSE
+ */
+public static const Cursor SIZENWSE;
+/**
+ * @see DWT#CURSOR_SIZEWE
+ */
+public static const Cursor SIZEWE;
+/**
+ * @see DWT#CURSOR_SIZENS
+ */
+public static const Cursor SIZENS;
+/**
+ * @see DWT#CURSOR_UPARROW
+ */
+public static const Cursor UPARROW;
+/**
+ * @see DWT#CURSOR_WAIT
+ */
+public static const Cursor WAIT;
+
+static this() {
+    ARROW   = new Cursor(null, DWT.CURSOR_ARROW);
+    SIZEN   = new Cursor(null, DWT.CURSOR_SIZEN);
+    SIZENE  = new Cursor(null, DWT.CURSOR_SIZENE);
+    SIZEE   = new Cursor(null, DWT.CURSOR_SIZEE);
+    SIZESE  = new Cursor(null, DWT.CURSOR_SIZESE);
+    SIZES   = new Cursor(null, DWT.CURSOR_SIZES);
+    SIZESW  = new Cursor(null, DWT.CURSOR_SIZESW);
+    SIZEW   = new Cursor(null, DWT.CURSOR_SIZEW);
+    SIZENW  = new Cursor(null, DWT.CURSOR_SIZENW);
+    SIZENS  = new Cursor(null, DWT.CURSOR_SIZENS);
+    SIZEWE  = new Cursor(null, DWT.CURSOR_SIZEWE);
+    APPSTARTING = new Cursor(null, DWT.CURSOR_APPSTARTING);
+    CROSS   = new Cursor(null, DWT.CURSOR_CROSS);
+    HAND    = new Cursor(null, DWT.CURSOR_HAND);
+    HELP    = new Cursor(null, DWT.CURSOR_HELP);
+    IBEAM   = new Cursor(null, DWT.CURSOR_IBEAM);
+    NO      = new Cursor(null, DWT.CURSOR_NO);
+    SIZEALL = new Cursor(null, DWT.CURSOR_SIZEALL);
+    SIZENESW = new Cursor(null, DWT.CURSOR_SIZENESW);
+    SIZENWSE = new Cursor(null, DWT.CURSOR_SIZENWSE);
+    UPARROW  = new Cursor(null, DWT.CURSOR_UPARROW);
+    WAIT     = new Cursor(null, DWT.CURSOR_WAIT);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/DefaultRangeModel.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,205 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.DefaultRangeModel;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Bean;
+import tango.text.convert.Format;
+
+import dwtx.draw2d.RangeModel;
+
+/**
+ * Generic implementation for a RangeModel.
+ *
+ *
+ *<pre>
+ *                    |<----extent--->|
+ *    ----|-----------|---------------|---------------|----
+ *       min          |                              max
+ *                  value
+ * </pre>
+ */
+public class DefaultRangeModel
+    : RangeModel
+{
+
+/**
+ * Listeners interested in the range model's property changes.
+ */
+protected PropertyChangeSupport propertyListeners;
+private int minimum = 0;
+private int maximum = 100;
+private int extent =  20;
+private int value = 0;
+
+public this(){
+    propertyListeners = new PropertyChangeSupport(this);
+}
+
+/**
+ * Registers the given listener as a PropertyChangeListener.
+ *
+ * @param listener the listener to be added
+ * @since 2.0
+ */
+public void addPropertyChangeListener(PropertyChangeListener listener) {
+    propertyListeners.addPropertyChangeListener(listener);
+}
+
+/**
+ * Notifies any listening PropertyChangeListeners that the property with the given id has
+ * changed.
+ *
+ * @param string the property name
+ * @param oldValue the old value
+ * @param newValue the new value
+ * @since 2.0
+ */
+protected void firePropertyChange(String string, int oldValue, int newValue) {
+    propertyListeners.firePropertyChange(string, oldValue, newValue);
+}
+
+/**
+ * @return the extent
+ */
+public int getExtent() {
+    return extent;
+}
+
+/**
+ * @return the maximum value
+ */
+public int getMaximum() {
+    return maximum;
+}
+
+/**
+ * @return the minimum value
+ */
+public int getMinimum() {
+    return minimum;
+}
+
+/**
+ * @return the current value
+ */
+public int getValue() {
+    return value;
+}
+
+/**
+ * @return whether the extent is between the minimum and maximum values
+ */
+public bool isEnabled() {
+    return (getMaximum() - getMinimum()) > getExtent();
+}
+
+/**
+ * Removes the given PropertyChangeListener from the list of listeners.
+ *
+ * @param listener the listener to be removed
+ */
+public void removePropertyChangeListener(PropertyChangeListener listener) {
+    propertyListeners.removePropertyChangeListener(listener);
+}
+
+/**
+ * @see dwtx.draw2d.RangeModel#setAll(int, int, int)
+ */
+public void setAll(int min, int ext, int max) {
+    int oldMin = minimum;
+    int oldExtent = extent;
+    int oldMax = maximum;
+    maximum = max;
+    minimum = min;
+    extent = ext;
+    if (oldMax !is max)
+        firePropertyChange(PROPERTY_MAXIMUM, oldMax, max);
+    if (oldExtent !is ext)
+        firePropertyChange(PROPERTY_EXTENT, oldExtent, ext);
+    if (oldMin !is min)
+        firePropertyChange(PROPERTY_MINIMUM, oldMin, min);
+    setValue(getValue());
+}
+
+/**
+ * Sets this RangeModel's extent and fires a property change if the given value is
+ * different from the current extent.
+ *
+ * @param extent the new extent value
+ */
+public void setExtent(int extent) {
+    if (this.extent is extent)
+        return;
+    int oldValue = this.extent;
+    this.extent = extent;
+    firePropertyChange(PROPERTY_EXTENT, oldValue, extent);
+    setValue(getValue());
+}
+
+/**
+ * Sets this RangeModel's maximum value and fires a property change if the given value is
+ * different from the current maximum value.
+ *
+ * @param maximum the new maximum value
+ */
+public void setMaximum(int maximum) {
+    if (this.maximum is maximum)
+        return;
+    int oldValue = this.maximum;
+    this.maximum = maximum;
+    firePropertyChange(PROPERTY_MAXIMUM, oldValue, maximum);
+    setValue(getValue());
+}
+
+/**
+ * Sets this RangeModel's minimum value and fires a property change if the given value is
+ * different from the current minimum value.
+ *
+ * @param minimum the new minumum value
+ */
+public void setMinimum(int minimum) {
+    if (this.minimum is minimum)
+        return;
+    int oldValue = this.minimum;
+    this.minimum = minimum;
+    firePropertyChange(PROPERTY_MINIMUM, oldValue, minimum);
+    setValue(getValue());
+}
+
+/**
+ * Sets this RangeModel's current value.  If the given value is greater than the maximum,
+ * the maximum value is used.  If the given value is less than the minimum, the minimum
+ * value is used.  If the adjusted value is different from the current value, a property
+ * change is fired.
+ *
+ * @param value the new value
+ */
+public void setValue(int value) {
+    value = Math.max(getMinimum(), Math.min(getMaximum() - getExtent(), value));
+    if (this.value is value)
+        return;
+    int oldValue = this.value;
+    this.value = value;
+    firePropertyChange(PROPERTY_VALUE, oldValue, value);
+}
+
+/**
+ * @see java.lang.Object#toString()
+ */
+public String toString() {
+    return Format( "{} ({}, {}, {}, {})", super.toString(), minimum, maximum //$NON-NLS-2$ //$NON-NLS-1$
+            , extent, value ); //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/DeferredUpdateManager.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,339 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.DeferredUpdateManager;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+import dwtx.dwtxhelper.Collection;
+
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.graphics.GC;
+import dwt.widgets.Display;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.UpdateManager;
+import dwtx.draw2d.GraphicsSource;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.SWTGraphics;
+
+/**
+ * An UpdateManager that asynchronously updates the affected figures.
+ */
+public class DeferredUpdateManager
+    : UpdateManager
+{
+alias UpdateManager.addDirtyRegion addDirtyRegion;
+
+/**
+ * Calls {@link DeferredUpdateManager#performUpdate()}.
+ */
+protected class UpdateRequest
+    : Runnable
+{
+    /**
+     * Calls {@link DeferredUpdateManager#performUpdate()}.
+     */
+    public void run() {
+        performUpdate();
+    }
+}
+private Rectangle damage;
+private Map dirtyRegions;
+
+private GraphicsSource graphicsSource;
+private List invalidFigures;
+private IFigure root;
+private bool updateQueued;
+
+private bool updating;
+private bool validating;
+private RunnableChain afterUpdate;
+
+private static class RunnableChain {
+    RunnableChain next;
+    Runnable run_;
+
+    this(Runnable run_, RunnableChain next) {
+        this.run_ = run_;
+        this.next = next;
+    }
+
+    void run() {
+        if (next !is null)
+            next.run();
+        run_.run();
+    }
+}
+
+/**
+ * Empty constructor.
+ */
+public this() {
+    invalidFigures = new ArrayList();
+    dirtyRegions = new HashMap();
+}
+
+/**
+ * Constructs a new DererredUpdateManager with the given GraphicsSource.
+ * @param gs the graphics source
+ */
+public this(GraphicsSource gs) {
+    this();
+    setGraphicsSource(gs);
+}
+
+/**
+ * Adds a dirty region (defined by the rectangle <i>x, y, w, h</i>) to the update queue.
+ * If the figure isn't visible or either the width or height are 0, the method returns
+ * without queueing the dirty region.
+ *
+ * @param figure the figure that contains the dirty region
+ * @param x the x coordinate of the dirty region
+ * @param y the y coordinate of the dirty region
+ * @param w the width of the dirty region
+ * @param h the height of the dirty region
+ */
+public synchronized void addDirtyRegion(IFigure figure, int x, int y, int w, int h) {
+    if (w is 0 || h is 0 || !figure.isShowing())
+        return;
+
+    Rectangle rect = cast(Rectangle)dirtyRegions.get(cast(Object)figure);
+    if (rect is null) {
+        rect = new Rectangle(x, y, w, h);
+        dirtyRegions.put(cast(Object)figure, rect);
+    } else
+        rect.union_(x, y, w, h);
+
+    queueWork();
+}
+
+/**
+ * Adds the given figure to the update queue.  Invalid figures will be validated before
+ * the damaged regions are repainted.
+ *
+ * @param f the invalid figure
+ */
+public synchronized void addInvalidFigure(IFigure f) {
+    if (invalidFigures.contains(cast(Object)f))
+        return;
+    queueWork();
+    invalidFigures.add(cast(Object)f);
+}
+
+/**
+ * Returns a Graphics object for the given region.
+ * @param region the region to be repainted
+ * @return the Graphics object
+ */
+protected Graphics getGraphics(Rectangle region) {
+    if (graphicsSource is null)
+        return null;
+    return graphicsSource.getGraphics(region);
+}
+
+void paint(GC gc) {
+    if (!validating) {
+        SWTGraphics graphics = new SWTGraphics(gc);
+        if (!updating) {
+            /**
+             * If a paint occurs not as part of an update, we should notify that the region
+             * is being painted. Otherwise, notification already occurs in repairDamage().
+             */
+            Rectangle rect = graphics.getClip(new Rectangle());
+            HashMap map = new HashMap();
+            map.put(cast(Object)root, rect);
+            firePainting(rect, map);
+        }
+        performValidation();
+        root.paint(graphics);
+        graphics.dispose();
+    } else {
+        /*
+         * If figures are being validated then we can simply
+         * add a dirty region here and update will repaint this region with other
+         * dirty regions when it gets to painting. We can't paint if we're not sure
+         * that all figures are valid.
+         */
+        addDirtyRegion(root, new Rectangle(gc.getClipping()));
+    }
+}
+
+/**
+ * Performs the update.  Validates the invalid figures and then repaints the dirty
+ * regions.
+ * @see #validateFigures()
+ * @see #repairDamage()
+ */
+public synchronized void performUpdate() {
+    if (isDisposed() || updating)
+        return;
+    updating = true;
+    try {
+        performValidation();
+        updateQueued = false;
+        repairDamage();
+        if (afterUpdate !is null) {
+            RunnableChain chain = afterUpdate;
+            afterUpdate = null;
+            chain.run(); //chain may queue additional Runnable.
+            if (afterUpdate !is null)
+                queueWork();
+        }
+    } finally {
+        updating = false;
+    }
+}
+
+/**
+ * @see UpdateManager#performValidation()
+ */
+public void performValidation() {
+    if (invalidFigures.isEmpty() || validating)
+        return;
+    try {
+        IFigure fig;
+        validating = true;
+        fireValidating();
+        for (int i = 0; i < invalidFigures.size(); i++) {
+            fig = cast(IFigure) invalidFigures.get(i);
+            invalidFigures.set(i, null);
+            fig.validate();
+        }
+    } finally {
+        invalidFigures.clear();
+        validating = false;
+    }
+}
+
+/**
+ * Adds the given exposed region to the update queue and then performs the update.
+ *
+ * @param exposed the exposed region
+ */
+public synchronized void performUpdate(Rectangle exposed) {
+    addDirtyRegion(root, exposed);
+    performUpdate();
+}
+
+/**
+ * Posts an {@link UpdateRequest} using {@link Display#asyncExec(Runnable)}.  If work has
+ * already been queued, a new request is not needed.
+ */
+protected void queueWork() {
+    if (!updateQueued) {
+        sendUpdateRequest();
+        updateQueued = true;
+    }
+}
+
+/**
+ * Fires the <code>UpdateRequest</code> to the current display asynchronously.
+ * @since 3.2
+ */
+protected void sendUpdateRequest() {
+    Display display = Display.getCurrent();
+    if (display is null) {
+        throw new DWTException(DWT.ERROR_THREAD_INVALID_ACCESS);
+    }
+    display.asyncExec(new UpdateRequest());
+}
+
+/**
+ * Releases the graphics object, which causes the GraphicsSource to flush.
+ * @param graphics the graphics object
+ */
+protected void releaseGraphics(Graphics graphics) {
+    graphics.dispose();
+    graphicsSource.flushGraphics(damage);
+}
+
+/**
+ * Repaints the dirty regions on the update queue and calls
+ * {@link UpdateManager#firePainting(Rectangle, Map)}, unless there are no dirty regions.
+ */
+protected void repairDamage() {
+    Iterator keys = dirtyRegions.keySet().iterator();
+    Rectangle contribution;
+    IFigure figure;
+    IFigure walker;
+
+    while (keys.hasNext()) {
+        figure = cast(IFigure)keys.next();
+        walker = figure.getParent();
+        contribution = cast(Rectangle)dirtyRegions.get(cast(Object)figure);
+        //A figure can't paint beyond its own bounds
+        contribution.intersect(figure.getBounds());
+        while (!contribution.isEmpty() && walker !is null) {
+            walker.translateToParent(contribution);
+            contribution.intersect(walker.getBounds());
+            walker = walker.getParent();
+        }
+        if (damage is null)
+            damage = new Rectangle(contribution);
+        else
+            damage.union_(contribution);
+    }
+
+    if (!dirtyRegions.isEmpty()) {
+        Map oldRegions = dirtyRegions;
+        dirtyRegions = new HashMap();
+        firePainting(damage, oldRegions);
+    }
+
+    if (damage !is null && !damage.isEmpty()) {
+        //ystem.out.println(damage);
+        Graphics graphics = getGraphics(damage);
+        if (graphics !is null) {
+            root.paint(graphics);
+            releaseGraphics(graphics);
+        }
+    }
+    damage = null;
+}
+
+/**
+ * Adds the given runnable and queues an update if an update is not under progress.
+ * @param runnable the runnable
+ */
+public synchronized void runWithUpdate(Runnable runnable) {
+    afterUpdate = new RunnableChain(runnable, afterUpdate);
+    if (!updating)
+        queueWork();
+}
+
+/**
+ * Sets the graphics source.
+ * @param gs the graphics source
+ */
+public void setGraphicsSource(GraphicsSource gs) {
+    graphicsSource = gs;
+}
+
+/**
+ * Sets the root figure.
+ * @param figure the root figure
+ */
+public void setRoot(IFigure figure) {
+    root = figure;
+}
+
+/**
+ * Validates all invalid figures on the update queue and calls
+ * {@link UpdateManager#fireValidating()} unless there are no invalid figures.
+ */
+protected void validateFigures() {
+    performValidation();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/DelegatingLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.DelegatingLayout;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.AbstractLayout;
+import dwtx.draw2d.Locator;
+
+/**
+ * Figures using a DelegatingLayout as their layout manager give
+ * location responsibilities to their children. The children
+ * of a Figure using a DelegatingLayout should have a
+ * {@link Locator Locator} as a constraint whose
+ * {@link Locator#relocate(IFigure target) relocate} method is
+ * responsible for placing the child.
+ */
+public class DelegatingLayout
+    : AbstractLayout
+{
+
+private Map constraints;
+
+this(){
+    constraints = new HashMap();
+}
+/**
+ * Calculates the preferred size of the given Figure.
+ * For the DelegatingLayout, this is the largest width and height
+ * values of the passed Figure's children.
+ *
+ * @param parent the figure whose preferred size is being calculated
+ * @param wHint the width hint
+ * @param hHint the height hint
+ * @return the preferred size
+ * @since 2.0
+ */
+protected Dimension calculatePreferredSize(IFigure parent, int wHint, int hHint) {
+    List children = parent.getChildren();
+    Dimension d = new Dimension();
+    for (int i = 0; i < children.size(); i++) {
+        IFigure child = cast(IFigure)children.get(i);
+        d.union_(child.getPreferredSize());
+    }
+    return d;
+}
+
+/**
+ * @see dwtx.draw2d.LayoutManager#getConstraint(dwtx.draw2d.IFigure)
+ */
+public Object getConstraint(IFigure child) {
+    return constraints.get(cast(Object)child);
+}
+
+/**
+ * Lays out the given figure's children based on their {@link Locator} constraint.
+ * @param parent the figure whose children should be layed out
+ */
+public void layout(IFigure parent) {
+    List children = parent.getChildren();
+    for (int i = 0; i < children.size(); i++) {
+        IFigure child = cast(IFigure)children.get(i);
+        Locator locator = cast(Locator)constraints.get(cast(Object)child);
+        if (locator !is null) {
+            locator.relocate(child);
+        }
+    }
+}
+
+/**
+ * Removes the locator for the given figure.
+ * @param child the child being removed
+ */
+public void remove(IFigure child) {
+    constraints.remove(cast(Object)child);
+}
+
+/**
+ * Sets the constraint for the given figure.
+ * @param figure the figure whose contraint is being set
+ * @param constraint the new constraint
+ */
+public void setConstraint(IFigure figure, Object constraint) {
+    super.setConstraint(figure, constraint);
+    if (constraint !is null)
+        constraints.put(cast(Object)figure, constraint);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Ellipse.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ *     Alex Selkov - Fix for Bug# 22701
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.Ellipse;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.Shape;
+import dwtx.draw2d.Graphics;
+
+/**
+ * An figure that draws an ellipse filling its bounds.
+ */
+public class Ellipse
+    : Shape
+{
+
+/**
+ * Constructs a new Ellipse with the default values of a Shape.
+ * @since 2.0
+ */
+public this() { }
+
+/**
+ * Returns <code>true</code> if the given point (x,y) is contained within this ellipse.
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @return <code>true</code>if the given point is contained
+ */
+public bool containsPoint(int x, int y) {
+    if (!super.containsPoint(x, y))
+        return false;
+    Rectangle r = getBounds();
+    long ux = x - r.x - r.width / 2;
+    long uy = y - r.y - r.height / 2;
+    return ((ux * ux) << 10) / (r.width * r.width)
+         + ((uy * uy) << 10) / (r.height * r.height) <= 256;
+}
+
+/**
+ * Fills the ellipse.
+ * @see dwtx.draw2d.Shape#fillShape(dwtx.draw2d.Graphics)
+ */
+protected void fillShape(Graphics graphics) {
+    graphics.fillOval(getBounds());
+}
+
+/**
+ * Outlines the ellipse.
+ * @see dwtx.draw2d.Shape#outlineShape(dwtx.draw2d.Graphics)
+ */
+protected void outlineShape(Graphics graphics) {
+    Rectangle r = Rectangle.SINGLETON;
+    r.setBounds(getBounds());
+    r.width--;
+    r.height--;
+    r.shrink((lineWidth - 1) / 2, (lineWidth - 1) / 2);
+    graphics.drawOval(r);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/EllipseAnchor.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.EllipseAnchor;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.AbstractConnectionAnchor;
+import dwtx.draw2d.IFigure;
+
+/**
+ * Similar to a {@link dwtx.draw2d.ChopboxAnchor}, except this anchor is located on
+ * the ellipse defined by the owners bounding box.
+ * @author Alex Selkov
+ * Created 31.08.2002 23:11:43
+ */
+public class EllipseAnchor : AbstractConnectionAnchor {
+
+/**
+ * @see dwtx.draw2d.AbstractConnectionAnchor#AbstractConnectionAnchor()
+ */
+public this() { }
+
+/**
+ * @see dwtx.draw2d.AbstractConnectionAnchor#AbstractConnectionAnchor(IFigure)
+ */
+public this(IFigure owner) {
+    super(owner);
+}
+
+/**
+ * Returns a point on the ellipse (defined by the owner's bounding box) where the
+ * connection should be anchored.
+ * @see dwtx.draw2d.ConnectionAnchor#getLocation(Point)
+ */
+public Point getLocation(Point reference) {
+    Rectangle r = Rectangle.SINGLETON;
+    r.setBounds(getOwner().getBounds());
+    r.translate(-1, -1);
+    r.resize(1, 1);
+    getOwner().translateToAbsolute(r);
+
+    Point ref_ = r.getCenter().negate().translate(reference);
+
+    if (ref_.x is 0)
+        return new Point(reference.x, (ref_.y > 0) ? r.bottom() : r.y);
+    if (ref_.y is 0)
+        return new Point((ref_.x > 0) ? r.right() : r.x, reference.y);
+
+    float dx = (ref_.x > 0) ? 0.5f : -0.5f;
+    float dy = (ref_.y > 0) ? 0.5f : -0.5f;
+
+    // ref.x, ref.y, r.width, r.height !is 0 => safe to proceed
+
+    float k = cast(float)(ref_.y * r.width) / (ref_.x * r.height);
+    k = k * k;
+
+    return r.getCenter().translate(cast(int)(r.width * dx / Math.sqrt(1 + k)),
+                                    cast(int)(r.height * dy / Math.sqrt(1 + 1 / k)));
+}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/EventDispatcher.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.EventDispatcher;
+
+import dwt.dwthelper.utils;
+
+import dwt.accessibility.AccessibleControlEvent;
+import dwt.accessibility.AccessibleControlListener;
+import dwt.accessibility.AccessibleListener;
+import dwt.events.FocusEvent;
+import dwt.events.KeyEvent;
+import dwt.events.MouseEvent;
+import dwt.events.TraverseEvent;
+import dwt.widgets.Control;
+import dwt.widgets.Event;
+
+import dwtx.draw2d.IFigure;
+
+/**
+ * Listens to various DWT events and dispatches these events to interested Draw2d objects.
+ */
+public abstract class EventDispatcher {
+
+    /**
+     * Combines {@link AccessibleControlListener} and {@link AccessibleListener}.
+     * Implements {@link AccessibleControlListener#getChild(AccessibleControlEvent)} to do
+     * nothing.
+     */
+    public abstract static class AccessibilityDispatcher
+        : AccessibleControlListener, AccessibleListener
+    {
+        /** @see AccessibleControlListener#getChild(AccessibleControlEvent) */
+        public void getChild(AccessibleControlEvent e) { }
+    }
+
+/**
+ * Dispatches a focus gained event.
+ * @param e the event
+ */
+public abstract void dispatchFocusGained(FocusEvent e);
+
+/**
+ * Dispatches a focus lost event.
+ * @param e the event
+ */
+public abstract void dispatchFocusLost(FocusEvent e);
+
+/**
+ * Dispatches a key pressed event.
+ * @param e the event
+ */
+public abstract void dispatchKeyPressed(KeyEvent e);
+
+/**
+ * Dispatches a key released event.
+ * @param e the event
+ */
+public abstract void dispatchKeyReleased(KeyEvent e);
+
+/**
+ * Dispatches a key traversed event.
+ * @param e the event
+ */
+public abstract void dispatchKeyTraversed(TraverseEvent e);
+
+/**
+ * Dispatches a mouse double clicked event.
+ * @param me the event
+ */
+public abstract void dispatchMouseDoubleClicked(MouseEvent me);
+
+/**
+ * Dispatches a mouse entered event.
+ * @param e the event
+ */
+public abstract void dispatchMouseEntered(MouseEvent e);
+
+/**
+ * Dispatches a mouse exited event.
+ * @param e the event
+ */
+public abstract void dispatchMouseExited(MouseEvent e);
+
+/**
+ * Dispatches a mouse hover event.
+ * @param me the event
+ */
+public abstract void dispatchMouseHover(MouseEvent me);
+
+/**
+ * Dispatches a moved event event.
+ * @param me the event
+ */
+public abstract void dispatchMouseMoved(MouseEvent me);
+
+/**
+ * Dispatches a mouse pressed event.
+ * @param me the event
+ */
+public abstract void dispatchMousePressed(MouseEvent me);
+
+/**
+ * Dispatches a mouse released event.
+ * @param me the event
+ */
+public abstract void dispatchMouseReleased(MouseEvent me);
+
+/**
+ * Dispatches a MouseWheel event. Does nothing by default.
+ * @param event the DWT event
+ * @since 3.1
+ */
+public void dispatchMouseWheelScrolled(Event event) { }
+
+/**
+ * Returns the AccessibilityDispatcher.
+ * @return the AccessibilityDispatcher
+ */
+protected abstract AccessibilityDispatcher getAccessibilityDispatcher();
+package AccessibilityDispatcher getAccessibilityDispatcher_package(){
+    return getAccessibilityDispatcher();
+}
+
+/**
+ * @return the IFigure that currently has focus
+ */
+/*package*/ abstract IFigure getFocusOwner();
+
+/**
+ * @return whether events are captured by a figure
+ */
+public abstract bool isCaptured();
+
+/**
+ * Releases capture initiated by {@link #setCapture(IFigure)}.
+ */
+protected abstract void releaseCapture();
+
+/**
+ * Requests focus for the given figure.
+ * @param fig the figure requesting focus
+ */
+public abstract void requestFocus(IFigure fig);
+
+/**
+ * Requests focus to be removed from the given figure.
+ * @param fig the figure requesting focus be removed
+ */
+public abstract void requestRemoveFocus(IFigure fig);
+
+/**
+ * Sets capture to the given figure.  All subsequent events will be sent to the given
+ * figure until {@link #releaseCapture()} is called.
+ *
+ * @param figure the figure capturing the events
+ */
+protected abstract void setCapture(IFigure figure);
+
+/**
+ * Sets the contol associated with this event dispatcher.
+ * @param control the control
+ */
+public abstract void setControl(Control control);
+
+/**
+ * Sets the root figure for this dispatcher.
+ * @param figure the root figure
+ */
+public abstract void setRoot(IFigure figure);
+
+/**
+ * Updates the cursor.
+ */
+protected abstract void updateCursor();
+
+package void updateCursor_package(){
+    updateCursor();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/EventListenerList.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.EventListenerList;
+
+import dwt.dwthelper.utils;
+
+import dwtx.dwtxhelper.Collection;
+
+/**
+ * This class is intended for internal use only.
+ * TODO: If this is for internal use only, we should move it to the internal package.
+ */
+public final class EventListenerList {
+
+private /+volatile+/ Object array[];
+
+/**
+ * Adds a listener of type <i>c</i> to the list.
+ * @param c the class
+ * @param listener the listener
+ */
+public synchronized void addListener(ClassInfo c, Object listener) {
+    if (listener is null || c is null)
+        throw new IllegalArgumentException("");
+
+    int oldSize = (array is null) ? 0 : array.length;
+    Object[] newArray = new Object[oldSize + 2];
+    if (oldSize !is 0)
+        System.arraycopy(array, 0, newArray, 0, oldSize);
+    newArray[oldSize++] = c;
+    newArray[oldSize] = listener;
+    array = newArray;
+}
+
+/**
+ * Returns <code>true</code> if this list of listeners contains a listener of type
+ * <i>c</i>.
+ * @param c the type
+ * @return whether this list contains a listener of type <i>c</i>
+ */
+public synchronized bool containsListener(ClassInfo c) {
+    if (array is null)
+        return false;
+    for (int i = 0; i < array.length; i += 2)
+        if (array[i] is c)
+            return true;
+    return false;
+}
+
+static class TypeIterator : Iterator {
+    private final Object[] items;
+    private final ClassInfo type;
+    private int index;
+    this(Object items[], ClassInfo type) {
+        this.items = items;
+        this.type = type;
+    }
+    public Object next() {
+        Object result = items[index + 1];
+        index += 2;
+        return result;
+    }
+
+    public bool hasNext() {
+        if (items is null)
+            return false;
+        while (index < items.length && items[index] !is type)
+            index += 2;
+        return index < items.length;
+    }
+
+    public void remove() {
+        throw new UnsupportedOperationException("Iterator removal not supported"); //$NON-NLS-1$
+    }
+}
+
+/**
+ * Returns an Iterator of all the listeners of type <i>c</i>.
+ * @param listenerType the type
+ * @return an Iterator of all the listeners of type <i>c</i>
+ */
+public synchronized Iterator getListeners(ClassInfo listenerType) {
+    return new TypeIterator(array, listenerType);
+}
+
+/**
+ * Removes the first <i>listener</i> of the specified type by identity.
+ * @param c the type
+ * @param listener the listener
+ */
+public synchronized void removeListener(ClassInfo c, Object listener) {
+    if (array is null || array.length is 0)
+        return;
+    if (listener is null || c is null)
+        throw new IllegalArgumentException("");
+
+    int index = 0;
+    while (index < array.length) {
+        if (array[index] is c && array[index + 1] is listener)
+            break;
+        index += 2;
+    }
+    if (index is array.length)
+        return; //listener was not found
+
+    Object newArray[] = new Object[array.length - 2];
+    System.arraycopy(array, 0, newArray, 0, index);
+    System.arraycopy(array, index + 2, newArray, index, array.length - index - 2);
+    array = newArray;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ExclusionSearch.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ExclusionSearch;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.TreeSearch;
+import dwtx.draw2d.IFigure;
+
+/**
+ * A <code>TreeSearch</code> that excludes figures contained in a {@link
+ * java.util.Collection}.
+ * @author hudsonr
+ * @since 2.1
+ */
+public class ExclusionSearch : TreeSearch {
+
+private const Collection c;
+
+/**
+ * Constructs an Exclusion search using the given collection.
+ * @param collection the exclusion set
+ */
+public this(Collection collection) {
+    this.c = collection;
+}
+
+/**
+ * @see dwtx.draw2d.TreeSearch#accept(IFigure)
+ */
+public bool accept(IFigure figure) {
+    //Prune is called before accept, so there is no reason to check the collection again.
+    return true;
+}
+
+/**
+ * Returns <code>true</code> if the figure is a member of the Collection.
+ * @see dwtx.draw2d.TreeSearch#prune(IFigure)
+ */
+public bool prune(IFigure f) {
+    return c.contains(cast(Object)f);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/FanRouter.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.FanRouter;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.geometry.Ray;
+import dwtx.draw2d.AutomaticRouter;
+import dwtx.draw2d.PositionConstants;
+
+/**
+ * Automatic router that spreads its {@link Connection Connections} in a fan-like fashion
+ * upon collision.
+ */
+public class FanRouter
+    : AutomaticRouter
+{
+
+private int separation = 10;
+
+/**
+ * Returns the separation in pixels between fanned connections.
+ *
+ * @return the separation
+ * @since 2.0
+ */
+public int getSeparation() {
+    return separation;
+}
+
+/**
+ * Modifies a given PointList that collides with some other PointList.  The given
+ * <i>index</i> indicates that this it the i<sup>th</sup> PointList in a group of
+ * colliding points.
+ *
+ * @param points the colliding points
+ * @param index the index
+ */
+protected void handleCollision(PointList points, int index) {
+    Point start = points.getFirstPoint();
+    Point end = points.getLastPoint();
+
+    if (start.opEquals(end))
+        return;
+
+    Point midPoint = new Point((end.x + start.x) / 2, (end.y + start.y) / 2);
+    int position = end.getPosition(start);
+    Ray ray;
+    if (position is PositionConstants.SOUTH || position is PositionConstants.EAST)
+        ray = new Ray(start, end);
+    else
+        ray = new Ray(end, start);
+    double length = ray.length();
+
+    double xSeparation = separation * ray.x / length;
+    double ySeparation = separation * ray.y / length;
+
+    Point bendPoint;
+
+    if (index % 2 is 0) {
+        bendPoint = new Point(
+            midPoint.x + (index / 2) * (-1 * ySeparation),
+            midPoint.y + (index / 2) * xSeparation);
+    } else {
+        bendPoint = new Point(
+            midPoint.x + (index / 2) * ySeparation,
+            midPoint.y + (index / 2) * (-1 * xSeparation));
+    }
+    if (!bendPoint.opEquals(midPoint))
+        points.insertPoint(bendPoint, 1);
+}
+
+/**
+ * Sets the colliding {@link Connection Connection's} separation in pixels.
+ *
+ * @param value the separation
+ * @since 2.0
+ */
+public void setSeparation(int value) {
+    separation = value;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Figure.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,1922 @@
+/*******************************************************************************
+ * 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.Figure;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Bean;
+import dwtx.dwtxhelper.Collection;
+
+import dwt.graphics.Color;
+import dwt.graphics.Cursor;
+import dwt.graphics.Font;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.geometry.Translatable;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.LayoutManager;
+import dwtx.draw2d.EventListenerList;
+import dwtx.draw2d.Border;
+import dwtx.draw2d.AncestorHelper;
+import dwtx.draw2d.CoordinateListener;
+import dwtx.draw2d.FigureListener;
+import dwtx.draw2d.FocusListener;
+import dwtx.draw2d.KeyListener;
+import dwtx.draw2d.MouseListener;
+import dwtx.draw2d.MouseMotionListener;
+import dwtx.draw2d.TreeSearch;
+import dwtx.draw2d.LayoutListener;
+import dwtx.draw2d.UpdateManager;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.AncestorListener;
+import dwtx.draw2d.MouseEvent;
+import dwtx.draw2d.EventDispatcher;
+import dwtx.draw2d.FocusEvent;
+import dwtx.draw2d.KeyEvent;
+import dwtx.draw2d.AncestorHelper;
+import dwtx.draw2d.ExclusionSearch;
+import dwtx.draw2d.AbstractBackground;
+import dwtx.draw2d.Orientable;
+import dwtx.draw2d.GraphicsSource;
+
+/**
+ * The base implementation for graphical figures.
+ */
+public class Figure
+    : IFigure
+{
+
+private static /+const+/ Rectangle PRIVATE_RECT;
+private static /+const+/ Point PRIVATE_POINT;
+private static const int
+    FLAG_VALID   = 1,
+    FLAG_OPAQUE  = (1 << 1),
+    FLAG_VISIBLE = (1 << 2),
+    FLAG_FOCUSABLE = (1 << 3),
+    FLAG_ENABLED = (1 << 4),
+    FLAG_FOCUS_TRAVERSABLE = (1 << 5);
+
+static const int
+    FLAG_REALIZED = 1 << 31;
+
+/**
+ * The largest flag defined in this class.  If subclasses define flags, they should
+ * declare them as larger than this value and redefine MAX_FLAG to be their largest flag
+ * value.
+ * <P>
+ * This constant is evaluated at runtime and will not be inlined by the compiler.
+ */
+protected static const int MAX_FLAG = FLAG_FOCUS_TRAVERSABLE;
+
+/**
+ * The rectangular area that this Figure occupies.
+ */
+protected Rectangle bounds;
+
+private LayoutManager layoutManager;
+
+/**
+ * The flags for this Figure.
+ */
+protected int flags = FLAG_VISIBLE | FLAG_ENABLED;
+
+private IFigure parent;
+private Cursor cursor;
+
+private PropertyChangeSupport propertyListeners;
+private EventListenerList eventListeners;
+
+private List children;
+
+/**
+ * This Figure's preferred size.
+ */
+protected Dimension prefSize;
+
+/**
+ * This Figure's minimum size.
+ */
+protected Dimension minSize;
+
+/**
+ * This Figure's maximum size.
+ */
+protected Dimension maxSize;
+
+/**
+ * @deprecated access using {@link #getLocalFont()}
+ */
+protected Font font;
+
+/**
+ * @deprecated access using {@link #getLocalBackgroundColor()}.
+ */
+protected Color bgColor;
+
+/**
+ * @deprecated access using {@link #getLocalForegroundColor()}.
+ */
+protected Color fgColor;
+
+/**
+ * @deprecated access using {@link #getBorder()}
+ */
+protected Border border;
+
+/**
+ * @deprecated access using {@link #getToolTip()}
+ */
+protected IFigure toolTip;
+
+private AncestorHelper ancestorHelper;
+
+private static void static_this(){
+    if( PRIVATE_RECT is null ){
+        synchronized( Figure.classinfo ){
+            if( PRIVATE_RECT is null ){
+                PRIVATE_RECT = new Rectangle();
+                PRIVATE_POINT = new Point();
+            }
+        }
+    }
+}
+
+private void instanceInit(){
+    static_this();
+    bounds = new Rectangle(0, 0, 0, 0);
+    eventListeners = new EventListenerList();
+    children = Collections.EMPTY_LIST;
+}
+
+this(){
+    instanceInit();
+}
+/**
+ * Calls {@link #add(IFigure, Object, int)} with -1 as the index.
+ * @see IFigure#add(IFigure, Object)
+ */
+public final void add(IFigure figure, Object constraint) {
+    add(figure, constraint, -1);
+}
+
+/**
+ * @see IFigure#add(IFigure, Object, int)
+ */
+public void add(IFigure figure, Object constraint, int index) {
+    if (children is Collections.EMPTY_LIST)
+        children = new ArrayList(2);
+    if (index < -1 || index > children.size())
+        throw new IndexOutOfBoundsException("Index does not exist"); //$NON-NLS-1$
+
+    //Check for Cycle in hierarchy
+    for (IFigure f = this; f !is null; f = f.getParent())
+        if (figure is f)
+            throw new IllegalArgumentException(
+                        "Figure being added introduces cycle"); //$NON-NLS-1$
+
+    //Detach the child from previous parent
+    if (figure.getParent() !is null)
+        figure.getParent().remove(figure);
+
+    if (index is -1)
+        children.add(cast(Object) figure);
+    else
+        children.add(index, cast(Object)figure);
+    figure.setParent(this);
+
+    if (layoutManager !is null)
+        layoutManager.setConstraint(figure, constraint);
+
+    revalidate();
+
+    if (getFlag(FLAG_REALIZED))
+        figure.addNotify();
+    figure.repaint();
+}
+
+/**
+ * Calls {@link #add(IFigure, Object, int)} with <code>null</code> as the constraint and
+ * -1 as the index.
+ * @see IFigure#add(IFigure)
+ */
+public final void add(IFigure figure) {
+    add(figure, null, -1);
+}
+
+/**
+ * Calls {@link #add(IFigure, Object, int)} with <code>null</code> as the constraint.
+ * @see IFigure#add(IFigure, int)
+ */
+public final void add(IFigure figure, int index) {
+    add(figure, null, index);
+}
+/**
+ * @see IFigure#addAncestorListener(AncestorListener)
+ */
+public void addAncestorListener(AncestorListener ancestorListener) {
+    if (ancestorHelper is null)
+        ancestorHelper = new AncestorHelper(this);
+    ancestorHelper.addAncestorListener(ancestorListener);
+}
+
+/**
+ * @see IFigure#addCoordinateListener(CoordinateListener)
+ */
+public void addCoordinateListener(CoordinateListener listener) {
+    eventListeners.addListener(CoordinateListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * @see IFigure#addFigureListener(FigureListener)
+ */
+public void addFigureListener(FigureListener listener) {
+    eventListeners.addListener(FigureListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * @see IFigure#addFocusListener(FocusListener)
+ */
+public void addFocusListener(FocusListener listener) {
+    eventListeners.addListener(FocusListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * @see IFigure#addKeyListener(KeyListener)
+ */
+public void addKeyListener(KeyListener listener) {
+    eventListeners.addListener(KeyListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * Appends the given layout listener to the list of layout listeners.
+ * @since 3.1
+ * @param listener the listener being added
+ */
+public void addLayoutListener(LayoutListener listener) {
+    if (auto n = cast(LayoutNotifier)layoutManager ) {
+        LayoutNotifier notifier = n;
+        notifier.listeners.add(cast(Object)listener);
+    } else
+        layoutManager = new LayoutNotifier(layoutManager, listener);
+}
+
+/**
+ * Adds a listener of type <i>clazz</i> to this Figure's list of event listeners.
+ * @param clazz The listener type
+ * @param listener The listener
+ */
+protected void addListener(ClassInfo clazz, Object listener) {
+    eventListeners.addListener(clazz, cast(Object)listener);
+}
+
+/**
+ * @see IFigure#addMouseListener(MouseListener)
+ */
+public void addMouseListener(MouseListener listener) {
+    eventListeners.addListener(MouseListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * @see IFigure#addMouseMotionListener(MouseMotionListener)
+ */
+public void addMouseMotionListener(MouseMotionListener listener) {
+    eventListeners.addListener(MouseMotionListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * Called after the receiver's parent has been set and it has been added to its parent.
+ *
+ * @since 2.0
+ */
+public void addNotify() {
+    if (getFlag(FLAG_REALIZED))
+        throw new RuntimeException("addNotify() should not be called multiple times"); //$NON-NLS-1$
+    setFlag(FLAG_REALIZED, true);
+    for (int i = 0; i < children.size(); i++)
+        (cast(IFigure)children.get(i)).addNotify();
+}
+
+/**
+ * @see IFigure#addPropertyChangeListener(String,
+ * PropertyChangeListener)
+ */
+public void addPropertyChangeListener(String property, PropertyChangeListener listener) {
+    if (propertyListeners is null)
+        propertyListeners = new PropertyChangeSupport(this);
+    propertyListeners.addPropertyChangeListener(property, listener);
+}
+
+/**
+ * @see IFigure#addPropertyChangeListener(PropertyChangeListener)
+ */
+public void addPropertyChangeListener(PropertyChangeListener listener) {
+    if (propertyListeners is null)
+        propertyListeners = new PropertyChangeSupport(this);
+    propertyListeners.addPropertyChangeListener(listener);
+}
+
+/**
+ * This method is final.  Override {@link #containsPoint(int, int)} if needed.
+ * @see IFigure#containsPoint(Point)
+ * @since 2.0
+ */
+public final bool containsPoint(Point p) {
+    return containsPoint(p.x, p.y);
+}
+
+/**
+ * @see IFigure#containsPoint(int, int)
+ */
+public bool containsPoint(int x, int y) {
+    return getBounds().contains(x, y);
+}
+
+/**
+ * @see IFigure#erase()
+ */
+public void erase() {
+    if (getParent() is null || !isVisible())
+        return;
+
+    Rectangle r = new Rectangle(getBounds());
+    getParent().translateToParent(r);
+    getParent().repaint(r.x, r.y, r.width, r.height);
+}
+
+/**
+ * Returns a descendant of this Figure such that the Figure returned contains the point
+ * (x, y), and is accepted by the given TreeSearch. Returns <code>null</code> if none
+ * found.
+ * @param x The X coordinate
+ * @param y The Y coordinate
+ * @param search the TreeSearch
+ * @return The descendant Figure at (x,y)
+ */
+protected IFigure findDescendantAtExcluding(int x, int y, TreeSearch search) {
+    PRIVATE_POINT.setLocation(x, y);
+    translateFromParent(PRIVATE_POINT);
+    if (!getClientArea(Rectangle.SINGLETON).contains(PRIVATE_POINT))
+        return null;
+
+    x = PRIVATE_POINT.x;
+    y = PRIVATE_POINT.y;
+    IFigure fig;
+    for (int i = children.size(); i > 0;) {
+        i--;
+        fig = cast(IFigure)children.get(i);
+        if (fig.isVisible()) {
+            fig = fig.findFigureAt(x, y, search);
+            if (fig !is null)
+                return fig;
+        }
+    }
+    //No descendants were found
+    return null;
+}
+
+/**
+ * @see IFigure#findFigureAt(Point)
+ */
+public final IFigure findFigureAt(Point pt) {
+    return findFigureAtExcluding(pt.x, pt.y, Collections.EMPTY_LIST);
+}
+
+/**
+ * @see IFigure#findFigureAt(int, int)
+ */
+public final IFigure findFigureAt(int x, int y) {
+    return findFigureAt(x, y, IdentitySearch.INSTANCE);
+}
+
+/**
+ * @see IFigure#findFigureAt(int, int, TreeSearch)
+ */
+public IFigure findFigureAt(int x, int y, TreeSearch search) {
+    if (!containsPoint(x, y))
+        return null;
+    if (search.prune(this))
+        return null;
+    IFigure child = findDescendantAtExcluding(x, y, search);
+    if (child !is null)
+        return child;
+    if (search.accept(this))
+        return this;
+    return null;
+}
+
+/**
+ * @see IFigure#findFigureAtExcluding(int, int, Collection)
+ */
+public final IFigure findFigureAtExcluding(int x, int y, Collection c) {
+    return findFigureAt(x, y, new ExclusionSearch(c));
+}
+
+/**
+ * Returns the deepest descendant for which {@link #isMouseEventTarget()} returns
+ * <code>true</code> or <code>null</code> if none found. The Parameters <i>x</i> and
+ * <i>y</i> are absolute locations. Any Graphics transformations applied by this Figure to
+ * its children during {@link #paintChildren(Graphics)} (thus causing the children to
+ * appear transformed to the user) should be applied inversely to the points <i>x</i> and
+ * <i>y</i> when called on the children.
+ *
+ * @param x The X coordinate
+ * @param y The Y coordinate
+ * @return The deepest descendant for which isMouseEventTarget() returns true
+ */
+public IFigure findMouseEventTargetAt(int x, int y) {
+    if (!containsPoint(x, y))
+        return null;
+    IFigure f = findMouseEventTargetInDescendantsAt(x, y);
+    if (f !is null)
+        return f;
+    if (isMouseEventTarget())
+        return this;
+    return null;
+}
+
+/**
+ * Searches this Figure's children for the deepest descendant for which
+ * {@link #isMouseEventTarget()} returns <code>true</code> and returns that descendant or
+ * <code>null</code> if none found.
+ * @see #findMouseEventTargetAt(int, int)
+ * @param x The X coordinate
+ * @param y The Y coordinate
+ * @return The deepest descendant for which isMouseEventTarget() returns true
+ */
+protected IFigure findMouseEventTargetInDescendantsAt(int x, int y) {
+    PRIVATE_POINT.setLocation(x, y);
+    translateFromParent(PRIVATE_POINT);
+
+    if (!getClientArea(Rectangle.SINGLETON).contains(PRIVATE_POINT))
+        return null;
+
+    IFigure fig;
+    for (int i = children.size(); i > 0;) {
+        i--;
+        fig = cast(IFigure)children.get(i);
+        if (fig.isVisible() && fig.isEnabled()) {
+            if (fig.containsPoint(PRIVATE_POINT.x, PRIVATE_POINT.y)) {
+                fig = fig.findMouseEventTargetAt(PRIVATE_POINT.x, PRIVATE_POINT.y);
+                return fig;
+            }
+        }
+    }
+    return null;
+}
+
+/**
+ * Notifies to all {@link CoordinateListener}s that this figure's local coordinate system
+ * has changed in a way which affects the absolute bounds of figures contained within.
+ *
+ * @since 3.1
+ */
+protected void fireCoordinateSystemChanged() {
+    if (!eventListeners.containsListener(CoordinateListener.classinfo))
+        return;
+    Iterator figureListeners = eventListeners.getListeners(CoordinateListener.classinfo);
+    while (figureListeners.hasNext())
+        (cast(CoordinateListener)figureListeners.next()).
+            coordinateSystemChanged(this);
+}
+
+/**
+ * Notifies to all {@link FigureListener}s that this figure has moved. Moved means
+ * that the bounds have changed in some way, location and/or size.
+ * @since 3.1
+ */
+protected void fireFigureMoved() {
+    if (!eventListeners.containsListener(FigureListener.classinfo))
+        return;
+    Iterator figureListeners = eventListeners.getListeners(FigureListener.classinfo);
+    while (figureListeners.hasNext())
+        (cast(FigureListener)figureListeners.next()).
+            figureMoved(this);
+}
+
+/**
+ * Fires both figuremoved and coordinate system changed. This method exists for
+ * compatibility. Some listeners which used to listen for figureMoved now listen for
+ * coordinates changed.  So to be sure that those new listeners are notified, any client
+ * code which used called this method will also result in notification of coordinate
+ * changes.
+ * @since 2.0
+ * @deprecated call fireFigureMoved() or fireCoordinateSystemChanged() as appropriate
+ */
+protected void fireMoved() {
+    fireFigureMoved();
+    fireCoordinateSystemChanged();
+}
+
+/**
+ * Notifies any {@link PropertyChangeListener PropertyChangeListeners} listening to this
+ * Figure that the bool property with id <i>property</i> has changed.
+ * @param property The id of the property that changed
+ * @param old The old value of the changed property
+ * @param current The current value of the changed property
+ * @since 2.0
+ */
+protected void firePropertyChange(String property, bool old, bool current) {
+    if (propertyListeners is null)
+        return;
+    propertyListeners.firePropertyChange(property, old, current);
+}
+
+/**
+ * Notifies any {@link PropertyChangeListener PropertyChangeListeners} listening to this
+ * figure that the Object property with id <i>property</i> has changed.
+ * @param property The id of the property that changed
+ * @param old The old value of the changed property
+ * @param current The current value of the changed property
+ * @since 2.0
+ */
+protected void firePropertyChange(String property, Object old, Object current) {
+    if (propertyListeners is null)
+        return;
+    propertyListeners.firePropertyChange(property, old, current);
+}
+
+/**
+ * Notifies any {@link PropertyChangeListener PropertyChangeListeners} listening to this
+ * figure that the integer property with id <code>property</code> has changed.
+ * @param property The id of the property that changed
+ * @param old The old value of the changed property
+ * @param current The current value of the changed property
+ * @since 2.0
+ */
+protected void firePropertyChange(String property, int old, int current) {
+    if (propertyListeners is null)
+        return;
+    propertyListeners.firePropertyChange(property, old, current);
+}
+
+/**
+ * Returns this Figure's background color.  If this Figure's background color is
+ * <code>null</code> and its parent is not <code>null</code>, the background color is
+ * inherited from the parent.
+ * @see IFigure#getBackgroundColor()
+ */
+public Color getBackgroundColor() {
+    if (bgColor is null && getParent() !is null)
+        return getParent().getBackgroundColor();
+    return bgColor;
+}
+
+/**
+ * @see IFigure#getBorder()
+ */
+public Border getBorder() {
+    return border;
+}
+
+/**
+ * Returns the smallest rectangle completely enclosing the figure. Implementors may return
+ * the Rectangle by reference. For this reason, callers of this method must not modify the
+ * returned Rectangle.
+ * @return The bounds of this Figure
+ */
+public Rectangle getBounds() {
+    return bounds;
+}
+
+/**
+ * @see IFigure#getChildren()
+ */
+public List getChildren() {
+    return children;
+}
+
+/**
+ * @see IFigure#getClientArea(Rectangle)
+ */
+public Rectangle getClientArea(Rectangle rect) {
+    rect.setBounds(getBounds());
+    rect.crop(getInsets());
+    if (useLocalCoordinates())
+        rect.setLocation(0, 0);
+    return rect;
+}
+
+/**
+ * @see IFigure#getClientArea()
+ */
+public final Rectangle getClientArea() {
+    return getClientArea(new Rectangle());
+}
+
+/**
+ * @see IFigure#getCursor()
+ */
+public Cursor getCursor() {
+    if (cursor is null && getParent() !is null)
+        return getParent().getCursor();
+    return cursor;
+}
+
+/**
+ * Returns the value of the given flag.
+ * @param flag The flag to get
+ * @return The value of the given flag
+ */
+protected bool getFlag(int flag) {
+    return (flags & flag) !is 0;
+}
+
+/**
+ * @see IFigure#getFont()
+ */
+public Font getFont() {
+    if (font !is null)
+        return font;
+    if (getParent() !is null)
+        return getParent().getFont();
+    return null;
+}
+
+/**
+ * @see IFigure#getForegroundColor()
+ */
+public Color getForegroundColor() {
+    if (fgColor is null && getParent() !is null)
+        return getParent().getForegroundColor();
+    return fgColor;
+}
+
+/**
+ * Returns the border's Insets if the border is set. Otherwise returns NO_INSETS, an
+ * instance of Insets with all 0s. Returns Insets by reference.  DO NOT Modify returned
+ * value. Cannot return null.
+ * @return This Figure's Insets
+ */
+public Insets getInsets() {
+    if (getBorder() !is null)
+        return getBorder().getInsets(this);
+    return IFigure_NO_INSETS;
+}
+
+/**
+ * @see IFigure#getLayoutManager()
+ */
+public LayoutManager getLayoutManager() {
+    if (auto n = cast(LayoutNotifier)layoutManager )
+        return n.realLayout;
+    return layoutManager;
+}
+
+/**
+ * Returns an Iterator over the listeners of type <i>clazz</i> that are listening to
+ * this Figure. If there are no listeners of type <i>clazz</i>, an empty iterator is
+ * returned.
+ * @param clazz The type of listeners to get
+ * @return An Iterator over the requested listeners
+ * @since 2.0
+ */
+protected Iterator getListeners(ClassInfo clazz) {
+    if (eventListeners is null)
+        return Collections.EMPTY_LIST.iterator();
+    return eventListeners.getListeners(clazz);
+}
+
+/**
+ * Returns <code>null</code> or the local background Color of this Figure. Does not
+ * inherit this Color from the parent.
+ * @return bgColor <code>null</code> or the local background Color
+ */
+public Color getLocalBackgroundColor() {
+    return bgColor;
+}
+
+/**
+ * Returns <code>null</code> or the local font setting for this figure.  Does not return
+ * values inherited from the parent figure.
+ * @return <code>null</code> or the local font
+ * @since 3.1
+ */
+protected Font getLocalFont() {
+    return font;
+}
+
+/**
+ * Returns <code>null</code> or the local foreground Color of this Figure. Does not
+ * inherit this Color from the parent.
+ * @return fgColor <code>null</code> or the local foreground Color
+ */
+public Color getLocalForegroundColor() {
+    return fgColor;
+}
+
+/**
+ * Returns the top-left corner of this Figure's bounds.
+ * @return The top-left corner of this Figure's bounds
+ * @since 2.0
+ */
+public final Point getLocation() {
+    return getBounds().getLocation();
+}
+
+/**
+ * @see IFigure#getMaximumSize()
+ */
+public Dimension getMaximumSize() {
+    if (maxSize !is null)
+        return maxSize;
+    return IFigure_MAX_DIMENSION;
+}
+
+/**
+ * @see IFigure#getMinimumSize()
+ */
+public final Dimension getMinimumSize() {
+    return getMinimumSize(-1, -1);
+}
+
+/**
+ * @see IFigure#getMinimumSize(int, int)
+ */
+public Dimension getMinimumSize(int wHint, int hHint) {
+    if (minSize !is null)
+        return minSize;
+    if (getLayoutManager() !is null) {
+        Dimension d = getLayoutManager().getMinimumSize(this, wHint, hHint);
+        if (d !is null)
+            return d;
+    }
+    return getPreferredSize(wHint, hHint);
+}
+
+/**
+ * @see IFigure#getParent()
+ */
+public IFigure getParent() {
+    return parent;
+}
+
+/**
+ * @see IFigure#getPreferredSize()
+ */
+public final Dimension getPreferredSize() {
+    return getPreferredSize(-1, -1);
+}
+
+/**
+ * @see IFigure#getPreferredSize(int, int)
+ */
+public Dimension getPreferredSize(int wHint, int hHint) {
+    if (prefSize !is null)
+        return prefSize;
+    if (getLayoutManager() !is null) {
+        Dimension d = getLayoutManager().getPreferredSize(this, wHint, hHint);
+        if (d !is null)
+            return d;
+    }
+    return getSize();
+}
+
+/**
+ * @see IFigure#getSize()
+ */
+public final Dimension getSize() {
+    return getBounds().getSize();
+}
+
+/**
+ * @see IFigure#getToolTip()
+ */
+public IFigure getToolTip() {
+    return toolTip;
+}
+
+/**
+ * @see IFigure#getUpdateManager()
+ */
+public UpdateManager getUpdateManager() {
+    if (getParent() !is null)
+        return getParent().getUpdateManager();
+    // Only happens when the figure has not been realized
+    return NO_MANAGER;
+}
+
+/**
+ * @see IFigure#handleFocusGained(FocusEvent)
+ */
+public void handleFocusGained(FocusEvent event) {
+    Iterator iter = eventListeners.getListeners(FocusListener.classinfo);
+    while (iter.hasNext())
+        (cast(FocusListener)iter.next()).
+            focusGained(event);
+}
+
+/**
+ * @see IFigure#handleFocusLost(FocusEvent)
+ */
+public void handleFocusLost(FocusEvent event) {
+    Iterator iter = eventListeners.getListeners(FocusListener.classinfo);
+    while (iter.hasNext())
+        (cast(FocusListener)iter.next()).
+            focusLost(event);
+}
+
+/**
+ * @see IFigure#handleKeyPressed(KeyEvent)
+ */
+public void handleKeyPressed(KeyEvent event) {
+    Iterator iter = eventListeners.getListeners(KeyListener.classinfo);
+    while (!event.isConsumed() && iter.hasNext())
+        (cast(KeyListener)iter.next()).
+            keyPressed(event);
+}
+
+/**
+ * @see IFigure#handleKeyReleased(KeyEvent)
+ */
+public void handleKeyReleased(KeyEvent event) {
+    Iterator iter = eventListeners.getListeners(KeyListener.classinfo);
+    while (!event.isConsumed() && iter.hasNext())
+        (cast(KeyListener)iter.next()).
+            keyReleased(event);
+}
+
+/**
+ * @see IFigure#handleMouseDoubleClicked(MouseEvent)
+ */
+public void handleMouseDoubleClicked(MouseEvent event) {
+    Iterator iter = eventListeners.getListeners(MouseListener.classinfo);
+    while (!event.isConsumed() && iter.hasNext())
+        (cast(MouseListener)iter.next()).
+            mouseDoubleClicked(event);
+}
+
+/**
+ * @see IFigure#handleMouseDragged(MouseEvent)
+ */
+public void handleMouseDragged(MouseEvent event) {
+    Iterator iter = eventListeners.getListeners(MouseMotionListener.classinfo);
+    while (!event.isConsumed() && iter.hasNext())
+        (cast(MouseMotionListener)iter.next()).
+            mouseDragged(event);
+}
+
+/**
+ * @see IFigure#handleMouseEntered(MouseEvent)
+ */
+public void handleMouseEntered(MouseEvent event) {
+    Iterator iter = eventListeners.getListeners(MouseMotionListener.classinfo);
+    while (!event.isConsumed() && iter.hasNext())
+        (cast(MouseMotionListener)iter.next()).
+            mouseEntered(event);
+}
+
+/**
+ * @see IFigure#handleMouseExited(MouseEvent)
+ */
+public void handleMouseExited(MouseEvent event) {
+    Iterator iter = eventListeners.getListeners(MouseMotionListener.classinfo);
+    while (!event.isConsumed() && iter.hasNext())
+        (cast(MouseMotionListener)iter.next()).
+            mouseExited(event);
+}
+
+/**
+ * @see IFigure#handleMouseHover(MouseEvent)
+ */
+public void handleMouseHover(MouseEvent event) {
+    Iterator iter = eventListeners.getListeners(MouseMotionListener.classinfo);
+    while (!event.isConsumed() && iter.hasNext())
+        (cast(MouseMotionListener)iter.next()).
+            mouseHover(event);
+}
+
+/**
+ * @see IFigure#handleMouseMoved(MouseEvent)
+ */
+public void handleMouseMoved(MouseEvent event) {
+    Iterator iter = eventListeners.getListeners(MouseMotionListener.classinfo);
+    while (!event.isConsumed() && iter.hasNext())
+        (cast(MouseMotionListener)iter.next()).
+            mouseMoved(event);
+}
+
+/**
+ * @see IFigure#handleMousePressed(MouseEvent)
+ */
+public void handleMousePressed(MouseEvent event) {
+    Iterator iter = eventListeners.getListeners(MouseListener.classinfo);
+    while (!event.isConsumed() && iter.hasNext())
+        (cast(MouseListener)iter.next()).
+            mousePressed(event);
+}
+
+/**
+ * @see IFigure#handleMouseReleased(MouseEvent)
+ */
+public void handleMouseReleased(MouseEvent event) {
+    Iterator iter = eventListeners.getListeners(MouseListener.classinfo);
+    while (!event.isConsumed() && iter.hasNext())
+        (cast(MouseListener)iter.next()).
+            mouseReleased(event);
+}
+
+/**
+ * @see IFigure#hasFocus()
+ */
+public bool hasFocus() {
+    EventDispatcher dispatcher = internalGetEventDispatcher();
+    if (dispatcher is null)
+        return false;
+    return dispatcher.getFocusOwner() is this;
+}
+
+/**
+ * @see IFigure#internalGetEventDispatcher()
+ */
+public EventDispatcher internalGetEventDispatcher() {
+    if (getParent() !is null)
+        return getParent().internalGetEventDispatcher();
+    return null;
+}
+
+/**
+ * @see IFigure#intersects(Rectangle)
+ */
+public bool intersects(Rectangle rect) {
+    return getBounds().intersects(rect);
+}
+
+/**
+ * @see IFigure#invalidate()
+ */
+public void invalidate() {
+    if (layoutManager !is null)
+        layoutManager.invalidate();
+    setValid(false);
+}
+
+/**
+ * @see IFigure#invalidateTree()
+ */
+public void invalidateTree() {
+    invalidate();
+    for (Iterator iter = children.iterator(); iter.hasNext();) {
+        IFigure child = cast(IFigure) iter.next();
+        child.invalidateTree();
+    }
+}
+
+/**
+ * @see IFigure#isCoordinateSystem()
+ */
+public bool isCoordinateSystem() {
+    return useLocalCoordinates();
+}
+
+/**
+ * @see IFigure#isEnabled()
+ */
+public bool isEnabled() {
+    return (flags & FLAG_ENABLED) !is 0;
+}
+
+/**
+ * @see IFigure#isFocusTraversable()
+ */
+public bool isFocusTraversable() {
+    return (flags & FLAG_FOCUS_TRAVERSABLE) !is 0;
+}
+
+/**
+ * Returns <code>true</code> if this Figure can receive {@link MouseEvent MouseEvents}.
+ * @return <code>true</code> if this Figure can receive {@link MouseEvent MouseEvents}
+ * @since 2.0
+ */
+protected bool isMouseEventTarget() {
+    return (eventListeners.containsListener(MouseListener.classinfo)
+        || eventListeners.containsListener(MouseMotionListener.classinfo));
+}
+
+/**
+ * @see dwtx.draw2d.IFigure#isMirrored()
+ */
+public bool isMirrored() {
+    if (getParent() !is null)
+        return getParent().isMirrored();
+    return false;
+}
+
+/**
+ * @see IFigure#isOpaque()
+ */
+public bool isOpaque() {
+    return (flags & FLAG_OPAQUE) !is 0;
+}
+
+/**
+ * @see IFigure#isRequestFocusEnabled()
+ */
+public bool isRequestFocusEnabled() {
+    return (flags & FLAG_FOCUSABLE) !is 0;
+}
+
+/**
+ * @see IFigure#isShowing()
+ */
+public bool isShowing() {
+    return isVisible()
+      && (getParent() is null
+        || getParent().isShowing());
+}
+
+/**
+ * Returns <code>true</code> if this Figure is valid.
+ * @return <code>true</code> if this Figure is valid
+ * @since 2.0
+ */
+protected bool isValid() {
+    return (flags & FLAG_VALID) !is 0;
+}
+
+/**
+ * Returns <code>true</code> if revalidating this Figure does not require revalidating its
+ * parent.
+ * @return <code>true</code> if revalidating this Figure doesn't require revalidating its
+ * parent.
+ * @since 2.0
+ */
+protected bool isValidationRoot() {
+    return false;
+}
+
+/**
+ * @see IFigure#isVisible()
+ */
+public bool isVisible() {
+    return getFlag(FLAG_VISIBLE);
+}
+
+/**
+ * Lays out this Figure using its {@link LayoutManager}.
+ *
+ * @since 2.0
+ */
+protected void layout() {
+    if (layoutManager !is null)
+        layoutManager.layout(this);
+}
+
+/**
+ * Paints this Figure and its children.
+ * @param graphics The Graphics object used for painting
+ * @see #paintFigure(Graphics)
+ * @see #paintClientArea(Graphics)
+ * @see #paintBorder(Graphics)
+ */
+public void paint(Graphics graphics) {
+    if (getLocalBackgroundColor() !is null)
+        graphics.setBackgroundColor(getLocalBackgroundColor());
+    if (getLocalForegroundColor() !is null)
+        graphics.setForegroundColor(getLocalForegroundColor());
+    if (font !is null)
+        graphics.setFont(font);
+
+    graphics.pushState();
+    try {
+        paintFigure(graphics);
+        graphics.restoreState();
+        paintClientArea(graphics);
+        paintBorder(graphics);
+    } finally {
+        graphics.popState();
+    }
+}
+
+/**
+ * Paints the border associated with this Figure, if one exists.
+ * @param graphics The Graphics used to paint
+ * @see Border#paint(IFigure, Graphics, Insets)
+ * @since 2.0
+ */
+protected void paintBorder(Graphics graphics) {
+    if (getBorder() !is null)
+        getBorder().paint(this, graphics, IFigure_NO_INSETS);
+}
+
+/**
+ * Paints this Figure's children. The caller must save the state of the graphics prior to
+ * calling this method, such that <code>graphics.restoreState()</code> may be called
+ * safely, and doing so will return the graphics to its original state when the method was
+ * entered.
+ * <P>
+ * This method must leave the Graphics in its original state upon return.
+ * @param graphics the graphics used to paint
+ * @since 2.0
+ */
+protected void paintChildren(Graphics graphics) {
+    IFigure child;
+
+    Rectangle clip = Rectangle.SINGLETON;
+    for (int i = 0; i < children.size(); i++) {
+        child = cast(IFigure)children.get(i);
+        if (child.isVisible() && child.intersects(graphics.getClip(clip))) {
+            graphics.clipRect(child.getBounds());
+            child.paint(graphics);
+            graphics.restoreState();
+        }
+    }
+}
+
+/**
+ * Paints this Figure's client area. The client area is typically defined as the anything
+ * inside the Figure's {@link Border} or {@link Insets}, and by default includes the
+ * children of this Figure. On return, this method must leave the given Graphics in its
+ * initial state.
+ * @param graphics The Graphics used to paint
+ * @since 2.0
+ */
+protected void paintClientArea(Graphics graphics) {
+    if (children.isEmpty())
+        return;
+
+    bool optimizeClip = getBorder() is null || getBorder().isOpaque();
+
+    if (useLocalCoordinates()) {
+        graphics.translate(
+            getBounds().x + getInsets().left,
+            getBounds().y + getInsets().top);
+        if (!optimizeClip)
+            graphics.clipRect(getClientArea(PRIVATE_RECT));
+        graphics.pushState();
+        paintChildren(graphics);
+        graphics.popState();
+        graphics.restoreState();
+    } else {
+        if (optimizeClip)
+            paintChildren(graphics);
+        else {
+            graphics.clipRect(getClientArea(PRIVATE_RECT));
+            graphics.pushState();
+            paintChildren(graphics);
+            graphics.popState();
+            graphics.restoreState();
+        }
+    }
+}
+
+/**
+ * Paints this Figure's primary representation, or background. Changes made to the
+ * graphics to the graphics current state will not affect the subsequent calls to {@link
+ * #paintClientArea(Graphics)} and {@link #paintBorder(Graphics)}. Furthermore, it is safe
+ * to call <code>graphics.restoreState()</code> within this method, and doing so will
+ * restore the graphics to its original state upon entry.
+ * @param graphics The Graphics used to paint
+ * @since 2.0
+ */
+protected void paintFigure(Graphics graphics) {
+    if (isOpaque())
+        graphics.fillRectangle(getBounds());
+    if (null !is cast(AbstractBackground)getBorder() )
+        (cast(AbstractBackground) getBorder()).paintBackground(this, graphics, IFigure_NO_INSETS);
+}
+
+/**
+ * Translates this Figure's bounds, without firing a move.
+ * @param dx The amount to translate horizontally
+ * @param dy The amount to translate vertically
+ * @see #translate(int, int)
+ * @since 2.0
+ */
+protected void primTranslate(int dx, int dy) {
+    bounds.x += dx;
+    bounds.y += dy;
+    if (useLocalCoordinates()) {
+        fireCoordinateSystemChanged();
+        return;
+    }
+    for (int i = 0; i < children.size(); i++)
+        (cast(IFigure)children.get(i)).translate(dx, dy);
+}
+
+/**
+ * Removes the given child Figure from this Figure's hierarchy and revalidates this
+ * Figure. The child Figure's {@link #removeNotify()} method is also called.
+ * @param figure The Figure to remove
+ */
+public void remove(IFigure figure) {
+    if ((figure.getParent() !is this))
+        throw new IllegalArgumentException(
+                "Figure is not a child"); //$NON-NLS-1$
+    if (getFlag(FLAG_REALIZED))
+        figure.removeNotify();
+    if (layoutManager !is null)
+        layoutManager.remove(figure);
+    // The updates in the UpdateManager *have* to be
+    // done asynchronously, else will result in
+    // incorrect dirty region corrections.
+    figure.erase();
+    figure.setParent(null);
+    children.remove(cast(Object)figure);
+    revalidate();
+}
+
+/**
+ * Removes all children from this Figure.
+ *
+ * @see #remove(IFigure)
+ * @since 2.0
+ */
+public void removeAll() {
+    List list = new ArrayList(getChildren());
+    for (int i = 0; i < list.size(); i++) {
+        remove(cast(IFigure)list.get(i));
+    }
+}
+
+/**
+ * @see IFigure#removeAncestorListener(AncestorListener)
+ */
+public void removeAncestorListener(AncestorListener listener) {
+    if (ancestorHelper !is null) {
+        ancestorHelper.removeAncestorListener(listener);
+        if (ancestorHelper.isEmpty()) {
+            ancestorHelper.dispose();
+            ancestorHelper = null;
+        }
+    }
+}
+
+/**
+ * @see IFigure#removeCoordinateListener(CoordinateListener)
+ */
+public void removeCoordinateListener(CoordinateListener listener) {
+    eventListeners.removeListener(CoordinateListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * @see IFigure#removeFigureListener(FigureListener)
+ */
+public void removeFigureListener(FigureListener listener) {
+    eventListeners.removeListener(FigureListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * @see IFigure#removeFocusListener(FocusListener)
+ */
+public void removeFocusListener(FocusListener listener) {
+    eventListeners.removeListener(FocusListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * @see IFigure#removeKeyListener(KeyListener)
+ */
+public void removeKeyListener(KeyListener listener) {
+    eventListeners.removeListener(KeyListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * Removes the first occurence of the given listener.
+ * @since 3.1
+ * @param listener the listener being removed
+ */
+public void removeLayoutListener(LayoutListener listener) {
+    if (auto notifier = cast(LayoutNotifier)layoutManager ) {
+        notifier.listeners.remove(cast(Object)listener);
+        if (notifier.listeners.isEmpty())
+            layoutManager = notifier.realLayout;
+    }
+}
+
+/**
+ * Removes <i>listener</i> of type <i>clazz</i> from this Figure's list of listeners.
+ * @param clazz The type of listener
+ * @param listener The listener to remove
+ * @since 2.0
+ */
+protected void removeListener(ClassInfo clazz, Object listener) {
+    if (eventListeners is null)
+        return;
+    eventListeners.removeListener(clazz, listener);
+}
+
+/**
+ * @see IFigure#removeMouseListener(MouseListener)
+ */
+public void removeMouseListener(MouseListener listener) {
+    eventListeners.removeListener(MouseListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * @see IFigure#removeMouseMotionListener(MouseMotionListener)
+ */
+public void removeMouseMotionListener(MouseMotionListener listener) {
+    eventListeners.removeListener(MouseMotionListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * Called prior to this figure's removal from its parent
+ */
+public void removeNotify() {
+    for (int i = 0; i < children.size(); i++)
+        (cast(IFigure)children.get(i)).removeNotify();
+    if (internalGetEventDispatcher() !is null)
+        internalGetEventDispatcher().requestRemoveFocus(this);
+    setFlag(FLAG_REALIZED, false);
+}
+
+/**
+ * @see IFigure#removePropertyChangeListener(PropertyChangeListener)
+ */
+public void removePropertyChangeListener(PropertyChangeListener listener) {
+    if (propertyListeners is null) return;
+    propertyListeners.removePropertyChangeListener(listener);
+}
+
+/**
+ * @see IFigure#removePropertyChangeListener(String, PropertyChangeListener)
+ */
+public void removePropertyChangeListener(
+    String property,
+    PropertyChangeListener listener) {
+    if (propertyListeners is null) return;
+    propertyListeners.removePropertyChangeListener(property, listener);
+}
+
+/**
+ * @see IFigure#repaint(Rectangle)
+ */
+public final void repaint(Rectangle rect) {
+    repaint(rect.x, rect.y, rect.width, rect.height);
+}
+
+/**
+ * @see IFigure#repaint(int, int, int, int)
+ */
+public void repaint(int x, int y, int w, int h) {
+    if (isVisible())
+        getUpdateManager().addDirtyRegion(this, x, y, w, h);
+}
+
+/**
+ * @see IFigure#repaint()
+ */
+public void repaint() {
+    repaint(getBounds());
+}
+
+/**
+ * @see IFigure#requestFocus()
+ */
+public final void requestFocus() {
+    if (!isRequestFocusEnabled() || hasFocus())
+        return;
+    EventDispatcher dispatcher = internalGetEventDispatcher();
+    if (dispatcher is null)
+        return;
+    dispatcher.requestFocus(this);
+}
+
+/**
+ * @see IFigure#revalidate()
+ */
+public void revalidate() {
+    invalidate();
+    if (getParent() is null || isValidationRoot())
+        getUpdateManager().addInvalidFigure(this);
+    else
+        getParent().revalidate();
+}
+
+/**
+ * @see IFigure#setBackgroundColor(Color)
+ */
+public void setBackgroundColor(Color bg) {
+    bgColor = bg;
+    repaint();
+}
+
+/**
+ * @see IFigure#setBorder(Border)
+ */
+public void setBorder(Border border) {
+    this.border = border;
+    revalidate();
+    repaint();
+}
+
+/**
+ * Sets the bounds of this Figure to the Rectangle <i>rect</i>. Note that <i>rect</i> is
+ * compared to the Figure's current bounds to determine what needs to be repainted and/or
+ * exposed and if validation is required. Since {@link #getBounds()} may return the
+ * current bounds by reference, it is not safe to modify that Rectangle and then call
+ * setBounds() after making modifications. The figure would assume that the bounds are
+ * unchanged, and no layout or paint would occur. For proper behavior, always use a copy.
+ * @param rect The new bounds
+ * @since 2.0
+ */
+public void setBounds(Rectangle rect) {
+    int x = bounds.x,
+        y = bounds.y;
+
+    bool resize = (rect.width !is bounds.width) || (rect.height !is bounds.height),
+          translate = (rect.x !is x) || (rect.y !is y);
+
+    if ((resize || translate) && isVisible())
+        erase();
+    if (translate) {
+        int dx = rect.x - x;
+        int dy = rect.y - y;
+        primTranslate(dx, dy);
+    }
+
+    bounds.width = rect.width;
+    bounds.height = rect.height;
+
+    if (translate || resize) {
+        if (resize)
+            invalidate();
+        fireFigureMoved();
+        repaint();
+    }
+}
+
+/**
+ * Sets the direction of any {@link Orientable} children.  Allowable values for
+ * <code>dir</code> are found in {@link PositionConstants}.
+ * @param direction The direction
+ * @see Orientable#setDirection(int)
+ * @since 2.0
+ */
+protected void setChildrenDirection(int direction) {
+    FigureIterator iterator = new FigureIterator(this);
+    IFigure child;
+    while (iterator.hasNext()) {
+        child = iterator.nextFigure();
+        if ( auto c = cast(Orientable)child )
+            c.setDirection(direction);
+    }
+}
+
+/**
+ * Sets all childrens' enabled property to <i>value</i>.
+ * @param value The enable value
+ * @see #setEnabled(bool)
+ * @since 2.0
+ */
+protected void setChildrenEnabled(bool value) {
+    FigureIterator iterator = new FigureIterator(this);
+    while (iterator.hasNext())
+        iterator.nextFigure().setEnabled(value);
+}
+
+/**
+ * Sets the orientation of any {@link Orientable} children. Allowable values for
+ * <i>orientation</i> are found in {@link PositionConstants}.
+ * @param orientation The Orientation
+ * @see Orientable#setOrientation(int)
+ * @since 2.0
+ */
+protected void setChildrenOrientation(int orientation) {
+    FigureIterator iterator = new FigureIterator(this);
+    IFigure child;
+    while (iterator.hasNext()) {
+        child = iterator.nextFigure();
+        if (auto c = cast(Orientable)child )
+            c.setOrientation(orientation);
+    }
+}
+
+/**
+ * @see IFigure#setConstraint(IFigure, Object)
+ */
+public void setConstraint(IFigure child, Object constraint) {
+    if (child.getParent() !is this)
+        throw new IllegalArgumentException(
+            "Figure must be a child"); //$NON-NLS-1$
+
+    if (layoutManager !is null)
+        layoutManager.setConstraint(child, constraint);
+    revalidate();
+}
+
+/**
+ * @see IFigure#setCursor(Cursor)
+ */
+public void setCursor(Cursor cursor) {
+    if (this.cursor is cursor)
+        return;
+    this.cursor = cursor;
+    EventDispatcher dispatcher = internalGetEventDispatcher();
+    if (dispatcher !is null)
+        dispatcher.updateCursor_package();
+}
+
+/**
+ * @see IFigure#setEnabled(bool)
+ */
+public void setEnabled(bool value) {
+    if (isEnabled() is value)
+        return;
+    setFlag(FLAG_ENABLED, value);
+}
+
+/**
+ * Sets the given flag to the given value.
+ * @param flag The flag to set
+ * @param value The value
+ * @since 2.0
+ */
+protected final void setFlag(int flag, bool value) {
+    if (value)
+        flags |= flag;
+    else
+        flags &= ~flag;
+}
+
+/**
+ * @see IFigure#setFocusTraversable(bool)
+ */
+public void setFocusTraversable(bool focusTraversable) {
+    if (isFocusTraversable() is focusTraversable)
+        return;
+    setFlag(FLAG_FOCUS_TRAVERSABLE, focusTraversable);
+}
+
+/**
+ * @see IFigure#setFont(Font)
+ */
+public void setFont(Font f) {
+    if (font !is f) {
+        font = f;
+        revalidate();
+        repaint();
+    }
+}
+
+/**
+ * @see IFigure#setForegroundColor(Color)
+ */
+public void setForegroundColor(Color fg) {
+    if (fgColor !is null && fgColor.opEquals(fg))
+        return;
+    fgColor = fg;
+    repaint();
+}
+
+/**
+ * @see IFigure#setLayoutManager(LayoutManager)
+ */
+public void setLayoutManager(LayoutManager manager) {
+    if (auto n = cast(LayoutNotifier)layoutManager )
+        n.realLayout = manager;
+    else
+        layoutManager = manager;
+    revalidate();
+}
+
+/**
+ * @see IFigure#setLocation(Point)
+ */
+public void setLocation(Point p) {
+    if (getLocation().opEquals(p))
+        return;
+    Rectangle r = new Rectangle(getBounds());
+    r.setLocation(p);
+    setBounds(r);
+}
+
+/**
+ * @see IFigure#setMaximumSize(Dimension)
+ */
+public void setMaximumSize(Dimension d) {
+    if (maxSize !is null && maxSize.opEquals(d))
+        return;
+    maxSize = d;
+    revalidate();
+}
+
+/**
+ * @see IFigure#setMinimumSize(Dimension)
+ */
+public void setMinimumSize(Dimension d) {
+    if (minSize !is null && minSize.opEquals(d))
+        return;
+    minSize = d;
+    revalidate();
+}
+
+/**
+ * @see IFigure#setOpaque(bool)
+ */
+public void setOpaque(bool opaque) {
+    if (isOpaque() is opaque)
+        return;
+    setFlag(FLAG_OPAQUE, opaque);
+    repaint();
+}
+
+/**
+ * @see IFigure#setParent(IFigure)
+ */
+public void setParent(IFigure p) {
+    IFigure oldParent = parent;
+    parent = p;
+    firePropertyChange("parent", cast(Object)oldParent, cast(Object)p);//$NON-NLS-1$
+}
+
+/**
+ * @see IFigure#setPreferredSize(Dimension)
+ */
+public void setPreferredSize(Dimension size) {
+    if (prefSize !is null && prefSize.opEquals(size))
+        return;
+    prefSize = size;
+    revalidate();
+}
+
+/**
+ * Sets the preferred size of this figure.
+ * @param w The new preferred width
+ * @param h The new preferred height
+ * @see #setPreferredSize(Dimension)
+ * @since 2.0
+ */
+public final void setPreferredSize(int w, int h) {
+    setPreferredSize(new Dimension(w, h));
+}
+
+/**
+ * @see IFigure#setRequestFocusEnabled(bool)
+ */
+public void setRequestFocusEnabled(bool requestFocusEnabled) {
+    if (isRequestFocusEnabled() is requestFocusEnabled)
+        return;
+    setFlag(FLAG_FOCUSABLE, requestFocusEnabled);
+}
+
+/**
+ * @see IFigure#setSize(Dimension)
+ */
+public final void setSize(Dimension d) {
+    setSize(d.width, d.height);
+}
+
+/**
+ * @see IFigure#setSize(int, int)
+ */
+public void setSize(int w, int h) {
+    Rectangle bounds = getBounds();
+    if (bounds.width is w && bounds.height is h)
+        return;
+    Rectangle r = new Rectangle(getBounds());
+    r.setSize(w, h);
+    setBounds(r);
+}
+
+/**
+ * @see IFigure#setToolTip(IFigure)
+ */
+public void setToolTip(IFigure f) {
+    if (toolTip is f)
+        return;
+    toolTip = f;
+}
+
+/**
+ * Sets this figure to be valid if <i>value</i> is <code>true</code> and invalid
+ * otherwise.
+ * @param value The valid value
+ * @since 2.0
+ */
+public void setValid(bool value) {
+    setFlag(FLAG_VALID, value);
+}
+
+/**
+ * @see IFigure#setVisible(bool)
+ */
+public void setVisible(bool visible) {
+    bool currentVisibility = isVisible();
+    if (visible is currentVisibility)
+        return;
+    if (currentVisibility)
+        erase();
+    setFlag(FLAG_VISIBLE, visible);
+    if (visible)
+        repaint();
+    revalidate();
+}
+
+/**
+ * @see IFigure#translate(int, int)
+ */
+public final void translate(int x, int y) {
+    primTranslate(x, y);
+    fireFigureMoved();
+}
+
+/**
+ * @see IFigure#translateFromParent(Translatable)
+ */
+public void translateFromParent(Translatable t) {
+    if (useLocalCoordinates())
+        t.performTranslate(
+            -getBounds().x - getInsets().left,
+            -getBounds().y - getInsets().top);
+}
+
+/**
+ * @see IFigure#translateToAbsolute(Translatable)
+ */
+public final void translateToAbsolute(Translatable t) {
+    if (getParent() !is null) {
+        getParent().translateToParent(t);
+        getParent().translateToAbsolute(t);
+    }
+}
+
+/**
+ * @see IFigure#translateToParent(Translatable)
+ */
+public void translateToParent(Translatable t) {
+    if (useLocalCoordinates())
+        t.performTranslate(
+            getBounds().x + getInsets().left,
+            getBounds().y + getInsets().top);
+}
+
+/**
+ * @see IFigure#translateToRelative(Translatable)
+ */
+public final void translateToRelative(Translatable t) {
+    if (getParent() !is null) {
+        getParent().translateToRelative(t);
+        getParent().translateFromParent(t);
+    }
+}
+
+/**
+ * Returns <code>true</code> if this Figure uses local coordinates. This means its
+ * children are placed relative to this Figure's top-left corner.
+ * @return <code>true</code> if this Figure uses local coordinates
+ * @since 2.0
+ */
+protected bool useLocalCoordinates() {
+    return false;
+}
+
+/**
+ * @see IFigure#validate()
+ */
+public void validate() {
+    if (isValid())
+        return;
+    setValid(true);
+    layout();
+    for (int i = 0; i < children.size(); i++)
+        (cast(IFigure)children.get(i)).validate();
+}
+
+/**
+ * A search which does not filter any figures.
+ * since 3.0
+ */
+protected static final class IdentitySearch : TreeSearch {
+    /**
+     * The singleton instance.
+     */
+    public static const TreeSearch INSTANCE;
+    static this(){
+        INSTANCE = new IdentitySearch();
+    }
+    private this() { }
+    /**
+     * Always returns <code>true</code>.
+     * @see TreeSearch#accept(IFigure)
+     */
+    public bool accept(IFigure f) {
+        return true;
+    }
+    /**
+     * Always returns <code>false</code>.
+     * @see TreeSearch#prune(IFigure)
+     */
+    public bool prune(IFigure f) {
+        return false;
+    }
+}
+
+final class LayoutNotifier : LayoutManager {
+
+    LayoutManager realLayout;
+    List listeners;
+
+    this(LayoutManager layout, LayoutListener listener) {
+        listeners = new ArrayList(1);
+        realLayout = layout;
+        listeners.add(cast(Object)listener);
+    }
+
+    public Object getConstraint(IFigure child) {
+        if (realLayout !is null)
+            return realLayout.getConstraint(child);
+        return null;
+    }
+
+    public Dimension getMinimumSize(IFigure container, int wHint, int hHint) {
+        if (realLayout !is null)
+            return realLayout.getMinimumSize(container, wHint, hHint);
+        return null;
+    }
+
+    public Dimension getPreferredSize(IFigure container, int wHint, int hHint) {
+        if (realLayout !is null)
+            return realLayout.getPreferredSize(container, wHint, hHint);
+        return null;
+    }
+
+    public void invalidate() {
+        for (int i = 0; i < listeners.size(); i++)
+            (cast(LayoutListener)listeners.get(i)).invalidate(this.outer);
+
+        if (realLayout !is null)
+            realLayout.invalidate();
+    }
+
+    public void layout(IFigure container) {
+        bool consumed = false;
+        for (int i = 0; i < listeners.size(); i++)
+            consumed |= (cast(LayoutListener)listeners.get(i)).layout(container);
+
+        if (realLayout !is null && !consumed)
+            realLayout.layout(container);
+        for (int i = 0; i < listeners.size(); i++)
+            (cast(LayoutListener)listeners.get(i)).postLayout(container);
+    }
+
+    public void remove(IFigure child) {
+        for (int i = 0; i < listeners.size(); i++)
+            (cast(LayoutListener)listeners.get(i)).remove(child);
+        if (realLayout !is null)
+            realLayout.remove(child);
+    }
+
+    public void setConstraint(IFigure child, Object constraint) {
+        for (int i = 0; i < listeners.size(); i++)
+            (cast(LayoutListener)listeners.get(i)).setConstraint(child, constraint);
+        if (realLayout !is null)
+            realLayout.setConstraint(child, constraint);
+    }
+}
+
+/**
+ * Iterates over a Figure's children.
+ */
+public static class FigureIterator {
+    private List list;
+    private int index;
+    /**
+     * Constructs a new FigureIterator for the given Figure.
+     * @param figure The Figure whose children to iterate over
+     */
+    public this(IFigure figure) {
+        list = figure.getChildren();
+        index = list.size();
+    }
+    /**
+     * Returns the next Figure.
+     * @return The next Figure
+     */
+    public IFigure nextFigure() {
+        return cast(IFigure)list.get(--index);
+    }
+    /**
+     * Returns <code>true</code> if there's another Figure to iterate over.
+     * @return <code>true</code> if there's another Figure to iterate over
+     */
+    public bool hasNext() {
+        return index > 0;
+    }
+}
+
+/**
+ * An UpdateManager that does nothing.
+ */
+protected static const UpdateManager NO_MANAGER;
+static this(){
+    NO_MANAGER = new class() UpdateManager {
+        public void addDirtyRegion (IFigure figure, int x, int y, int w, int h) { }
+        public void addInvalidFigure(IFigure f) { }
+        public void performUpdate() { }
+        public void performUpdate(Rectangle region) { }
+        public void setRoot(IFigure root) { }
+        public void setGraphicsSource(GraphicsSource gs) { }
+    };
+}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/FigureCanvas.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,560 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.FigureCanvas;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Bean;
+static import dwtx.dwtxhelper.Collection;
+
+import dwt.DWT;
+import dwt.events.SelectionAdapter;
+import dwt.events.SelectionEvent;
+import dwt.graphics.Font;
+import dwt.widgets.Canvas;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.Viewport;
+import dwtx.draw2d.LightweightSystem;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Border;
+import dwtx.draw2d.RangeModel;
+static import dwt.graphics.Point;
+static import dwt.graphics.Rectangle;
+import dwtx.draw2d.UpdateListener;
+import dwtx.draw2d.ScrollPaneSolver;
+
+/**
+ * A Canvas that contains {@link Figure Figures}.
+ *
+ * <dl>
+ * <dt><b>Required Styles (when using certain constructors):</b></dt>
+ * <dd>V_SCROLL, H_SCROLL, NO_REDRAW_RESIZE</dd>
+ * <dt><b>Optional Styles:</b></dt>
+ * <dd>DOUBLE_BUFFERED, RIGHT_TO_LEFT, LEFT_TO_RIGHT, NO_BACKGROUND, BORDER</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the styles RIGHT_TO_LEFT, LEFT_TO_RIGHT may be specified.
+ * </p>
+ */
+public class FigureCanvas
+    : Canvas
+{
+
+private static final int ACCEPTED_STYLES =
+        DWT.RIGHT_TO_LEFT | DWT.LEFT_TO_RIGHT
+        | DWT.V_SCROLL | DWT.H_SCROLL
+        | DWT.NO_BACKGROUND
+        | DWT.NO_REDRAW_RESIZE
+        | DWT.DOUBLE_BUFFERED
+        | DWT.BORDER;
+
+/**
+ * The default styles are mixed in when certain constructors are used. This
+ * constant is a bitwise OR of the following DWT style constants:
+ * <UL>
+ *   <LI>{@link DWT#NO_REDRAW_RESIZE}</LI>
+ *   <LI>{@link DWT#NO_BACKGROUND}</LI>
+ *   <LI>{@link DWT#V_SCROLL}</LI>
+ *   <LI>{@link DWT#H_SCROLL}</LI>
+ * </UL>
+ */
+static const int DEFAULT_STYLES =
+        DWT.NO_REDRAW_RESIZE
+        | DWT.NO_BACKGROUND
+        | DWT.V_SCROLL
+        | DWT.H_SCROLL;
+
+private static const int REQUIRED_STYLES =
+        DWT.NO_REDRAW_RESIZE
+        | DWT.V_SCROLL
+        | DWT.H_SCROLL;
+
+/** Never show scrollbar */
+public static int NEVER = 0;
+/** Automatically show scrollbar when needed */
+public static int AUTOMATIC = 1;
+/** Always show scrollbar */
+public static int ALWAYS = 2;
+
+private int vBarVisibility;
+private int hBarVisibility;
+private Viewport viewport;
+private Font font;
+private int hBarOffset;
+private int vBarOffset;
+
+private PropertyChangeListener horizontalChangeListener;
+private PropertyChangeListener verticalChangeListener;
+private const LightweightSystem lws;
+
+/**
+ * Creates a new FigureCanvas with the given parent and the {@link #DEFAULT_STYLES}.
+ *
+ * @param parent the parent
+ */
+public this(Composite parent) {
+    this(parent, DWT.DOUBLE_BUFFERED, new LightweightSystem());
+}
+
+/**
+ * Constructor which applies the default styles plus any optional styles indicated.
+ * @param parent the parent composite
+ * @param style see the class javadoc for optional styles
+ * @since 3.1
+ */
+public this(Composite parent, int style) {
+    this(parent, style, new LightweightSystem());
+}
+
+/**
+ * Constructor which uses the given styles verbatim. Certain styles must be used
+ * with this class. Refer to the class javadoc for more details.
+ * @param style see the class javadoc for <b>required</b> and optional styles
+ * @param parent the parent composite
+ * @since 3.4
+ */
+public this(int style, Composite parent) {
+    this(style, parent, new LightweightSystem());
+}
+
+/**
+ * Constructs a new FigureCanvas with the given parent and LightweightSystem, using
+ * the {@link #DEFAULT_STYLES}.
+ * @param parent the parent
+ * @param lws the LightweightSystem
+ */
+public this(Composite parent, LightweightSystem lws) {
+    this(parent, DWT.DOUBLE_BUFFERED, lws);
+}
+
+/**
+ * Constructor taking a lightweight system and DWT style, which is used verbatim.
+ * Certain styles must be used with this class. Refer to the class javadoc for
+ * more details.
+ * @param style see the class javadoc for <b>required</b> and optional styles
+ * @param parent the parent composite
+ * @param lws the LightweightSystem
+ * @since 3.4
+ */
+public this(int style, Composite parent, LightweightSystem lws) {
+    this.lws = lws;
+    vBarVisibility = AUTOMATIC;
+    hBarVisibility = AUTOMATIC;
+
+    horizontalChangeListener = new class() PropertyChangeListener {
+        public void propertyChange(PropertyChangeEvent event) {
+            RangeModel model = getViewport().getHorizontalRangeModel();
+            hBarOffset = Math.max(0, -model.getMinimum());
+            getHorizontalBar().setValues(
+                model.getValue() + hBarOffset,
+                model.getMinimum() + hBarOffset,
+                model.getMaximum() + hBarOffset,
+                model.getExtent(),
+                Math.max(1, model.getExtent() / 20),
+                Math.max(1, model.getExtent() * 3 / 4));
+        }
+    };
+
+    verticalChangeListener = new class() PropertyChangeListener {
+        public void propertyChange(PropertyChangeEvent event) {
+            RangeModel model = getViewport().getVerticalRangeModel();
+            vBarOffset = Math.max(0, -model.getMinimum());
+            getVerticalBar().setValues(
+                model.getValue() + vBarOffset,
+                model.getMinimum() + vBarOffset,
+                model.getMaximum() + vBarOffset,
+                model.getExtent(),
+                Math.max(1, model.getExtent() / 20),
+                Math.max(1, model.getExtent() * 3 / 4));
+        }
+    };
+    super(parent, checkStyle(style));
+    getHorizontalBar().setVisible(false);
+    getVerticalBar().setVisible(false);
+    lws.setControl(this);
+    hook();
+}
+
+/**
+ * Constructor
+ * @param parent the parent composite
+ * @param style look at class javadoc for valid styles
+ * @param lws the lightweight system
+ * @since 3.1
+ */
+public this(Composite parent, int style, LightweightSystem lws) {
+    this(style | DEFAULT_STYLES, parent, lws);
+}
+
+private static int checkStyle(int style) {
+    if ((style & REQUIRED_STYLES) !is REQUIRED_STYLES)
+        throw new IllegalArgumentException("Required style missing on FigureCanvas"); //$NON-NLS-1$
+    if ((style & ~ACCEPTED_STYLES) !is 0)
+        throw new IllegalArgumentException("Invalid style being set on FigureCanvas"); //$NON-NLS-1$
+    return style;
+}
+
+/**
+ * @see dwt.widgets.Composite#computeSize(int, int, bool)
+ */
+public dwt.graphics.Point.Point computeSize(int wHint, int hHint, bool changed) {
+    // TODO not accounting for scrollbars and trim
+    Dimension size = getLightweightSystem().getRootFigure().getPreferredSize(wHint, hHint);
+    size.union_(new Dimension(wHint, hHint));
+    return new dwt.graphics.Point.Point(size.width, size.height);
+}
+
+/**
+ * @return the contents of the {@link Viewport}.
+ */
+public IFigure getContents() {
+    return getViewport().getContents();
+}
+
+/**
+ * @see dwt.widgets.Control#getFont()
+ */
+public Font getFont() {
+    if (font is null)
+        font = super.getFont();
+    return font;
+}
+
+/**
+ * @return the horizontal scrollbar visibility.
+ */
+public int getHorizontalScrollBarVisibility() {
+    return hBarVisibility;
+}
+
+/**
+ * @return the LightweightSystem
+ */
+public LightweightSystem getLightweightSystem() {
+    return lws;
+}
+
+/**
+ * @return the vertical scrollbar visibility.
+ */
+public int getVerticalScrollBarVisibility() {
+    return vBarVisibility;
+}
+
+/**
+ * Returns the Viewport.  If it's <code>null</code>, a new one is created.
+ * @return the viewport
+ */
+public Viewport getViewport() {
+    if (viewport is null)
+        setViewport(new Viewport(true));
+    return viewport;
+}
+
+/**
+ * Adds listeners for scrolling.
+ */
+private void hook() {
+    getLightweightSystem().getUpdateManager().addUpdateListener(new class() UpdateListener {
+        public void notifyPainting(Rectangle damage, dwtx.dwtxhelper.Collection.Map dirtyRegions) { }
+        public void notifyValidating() {
+            if (!isDisposed())
+                layoutViewport();
+        }
+    });
+
+    getHorizontalBar().addSelectionListener(new class() SelectionAdapter {
+        public void widgetSelected(SelectionEvent event) {
+            scrollToX(getHorizontalBar().getSelection() - hBarOffset);
+        }
+    });
+
+    getVerticalBar().addSelectionListener(new class() SelectionAdapter {
+        public void widgetSelected(SelectionEvent event) {
+            scrollToY(getVerticalBar().getSelection() - vBarOffset);
+        }
+    });
+}
+
+private void hookViewport() {
+    getViewport()
+        .getHorizontalRangeModel()
+        .addPropertyChangeListener(horizontalChangeListener);
+    getViewport()
+        .getVerticalRangeModel()
+        .addPropertyChangeListener(verticalChangeListener);
+}
+
+private void unhookViewport() {
+    getViewport()
+        .getHorizontalRangeModel()
+        .removePropertyChangeListener(horizontalChangeListener);
+    getViewport()
+        .getVerticalRangeModel()
+        .removePropertyChangeListener(verticalChangeListener);
+}
+
+private void layoutViewport() {
+    ScrollPaneSolver.Result result;
+    result = ScrollPaneSolver.solve((new Rectangle(getBounds())).setLocation(0, 0),
+        getViewport(),
+        getHorizontalScrollBarVisibility(),
+        getVerticalScrollBarVisibility(),
+        computeTrim(0, 0, 0, 0).width,
+        computeTrim(0, 0, 0, 0).height);
+    getLightweightSystem().setIgnoreResize(true);
+    try {
+        if (getHorizontalBar().getVisible() !is result.showH)
+            getHorizontalBar().setVisible(result.showH);
+        if (getVerticalBar().getVisible() !is result.showV)
+            getVerticalBar().setVisible(result.showV);
+        Rectangle r = new Rectangle(getClientArea());
+        r.setLocation(0, 0);
+        getLightweightSystem().getRootFigure().setBounds(r);
+    } finally {
+        getLightweightSystem().setIgnoreResize(false);
+    }
+}
+
+/**
+ * Scrolls in an animated way to the new x and y location.
+ * @param x the x coordinate to scroll to
+ * @param y the y coordinate to scroll to
+ */
+public void scrollSmoothTo(int x, int y) {
+    // Ensure newHOffset and newVOffset are within the appropriate ranges
+    x = verifyScrollBarOffset(getViewport().getHorizontalRangeModel(), x);
+    y = verifyScrollBarOffset(getViewport().getVerticalRangeModel(), y);
+
+    int oldX = getViewport().getViewLocation().x;
+    int oldY = getViewport().getViewLocation().y;
+    int dx = x - oldX;
+    int dy = y - oldY;
+
+    if (dx is 0 && dy is 0)
+        return; //Nothing to do.
+
+    Dimension viewingArea = getViewport().getClientArea().getSize();
+
+    int minFrames = 3;
+    int maxFrames = 6;
+    if (dx is 0 || dy is 0) {
+        minFrames = 6;
+        maxFrames = 13;
+    }
+    int frames = (Math.abs(dx) + Math.abs(dy)) / 15;
+    frames = Math.max(frames, minFrames);
+    frames = Math.min(frames, maxFrames);
+
+    int stepX = Math.min((dx / frames), (viewingArea.width / 3));
+    int stepY = Math.min((dy / frames), (viewingArea.height / 3));
+
+    for (int i = 1; i < frames; i++) {
+        scrollTo(oldX + i * stepX, oldY + i * stepY);
+        getViewport().getUpdateManager().performUpdate();
+    }
+    scrollTo(x, y);
+}
+
+/**
+ * Scrolls the contents to the new x and y location.  If this scroll operation only
+ * consists of a vertical or horizontal scroll, a call will be made to
+ * {@link #scrollToY(int)} or {@link #scrollToX(int)}, respectively, to increase
+ * performance.
+ *
+ * @param x the x coordinate to scroll to
+ * @param y the y coordinate to scroll to
+ */
+public void scrollTo(int x, int y) {
+    x = verifyScrollBarOffset(getViewport().getHorizontalRangeModel(), x);
+    y = verifyScrollBarOffset(getViewport().getVerticalRangeModel(), y);
+    if (x is getViewport().getViewLocation().x)
+        scrollToY(y);
+    else if (y is getViewport().getViewLocation().y)
+        scrollToX(x);
+    else
+        getViewport().setViewLocation(x, y);
+}
+/**
+ * Scrolls the contents horizontally so that they are offset by <code>hOffset</code>.
+ *
+ * @param hOffset the new horizontal offset
+ */
+public void scrollToX(int hOffset) {
+    hOffset = verifyScrollBarOffset(getViewport().getHorizontalRangeModel(), hOffset);
+    int hOffsetOld = getViewport().getViewLocation().x;
+    if (hOffset is hOffsetOld)
+        return;
+    int dx = -hOffset + hOffsetOld;
+
+    Rectangle clientArea = getViewport().getBounds().getCropped(getViewport().getInsets());
+    Rectangle blit = clientArea.getResized(-Math.abs(dx), 0);
+    Rectangle expose = clientArea.getCopy();
+    Point dest = clientArea.getTopLeft();
+    expose.width = Math.abs(dx);
+    if (dx < 0) { //Moving left?
+        blit.translate(-dx, 0); //Move blit area to the right
+        expose.x = dest.x + blit.width;
+    } else //Moving right
+        dest.x += dx; //Move expose area to the right
+
+    // fix for bug 41111
+    Control[] children = getChildren();
+    bool[] manualMove = new bool[children.length];
+    for (int i = 0; i < children.length; i++) {
+        dwt.graphics.Rectangle.Rectangle bounds = children[i].getBounds();
+        manualMove[i] = blit.width <= 0 || bounds.x > blit.x + blit.width
+                || bounds.y > blit.y + blit.height || bounds.x + bounds.width < blit.x
+                || bounds.y + bounds.height < blit.y;
+    }
+    scroll(dest.x, dest.y, blit.x, blit.y, blit.width, blit.height, true);
+    for (int i = 0; i < children.length; i++) {
+        if (children[i].isDisposed ())
+            continue;
+        dwt.graphics.Rectangle.Rectangle bounds = children[i].getBounds();
+        if (manualMove[i])
+            children[i].setBounds(bounds.x + dx, bounds.y, bounds.width, bounds.height);
+    }
+
+    getViewport().setIgnoreScroll(true);
+    getViewport().setHorizontalLocation(hOffset);
+    getViewport().setIgnoreScroll(false);
+    redraw(expose.x, expose.y, expose.width, expose.height, true);
+}
+
+/**
+ * Scrolls the contents vertically so that they are offset by <code>vOffset</code>.
+ *
+ * @param vOffset the new vertical offset
+ */
+public void scrollToY(int vOffset) {
+    vOffset = verifyScrollBarOffset(getViewport().getVerticalRangeModel(), vOffset);
+    int vOffsetOld = getViewport().getViewLocation().y;
+    if (vOffset is vOffsetOld)
+        return;
+    int dy = -vOffset + vOffsetOld;
+
+    Rectangle clientArea = getViewport().getBounds().getCropped(getViewport().getInsets());
+    Rectangle blit = clientArea.getResized(0, -Math.abs(dy));
+    Rectangle expose = clientArea.getCopy();
+    Point dest = clientArea.getTopLeft();
+    expose.height = Math.abs(dy);
+    if (dy < 0) { //Moving up?
+        blit.translate(0, -dy); //Move blit area down
+        expose.y = dest.y + blit.height; //Move expose area down
+    } else //Moving down
+        dest.y += dy;
+
+    // fix for bug 41111
+    Control[] children = getChildren();
+    bool[] manualMove = new bool[children.length];
+    for (int i = 0; i < children.length; i++) {
+        dwt.graphics.Rectangle.Rectangle bounds = children[i].getBounds();
+        manualMove[i] = blit.height <= 0 || bounds.x > blit.x + blit.width
+                || bounds.y > blit.y + blit.height || bounds.x + bounds.width < blit.x
+                || bounds.y + bounds.height < blit.y;
+    }
+    scroll(dest.x, dest.y, blit.x, blit.y, blit.width, blit.height, true);
+    for (int i = 0; i < children.length; i++) {
+        if (children[i].isDisposed ())
+            continue;
+        dwt.graphics.Rectangle.Rectangle bounds = children[i].getBounds();
+        if (manualMove[i])
+            children[i].setBounds(bounds.x, bounds.y + dy, bounds.width, bounds.height);
+    }
+
+    getViewport().setIgnoreScroll(true);
+    getViewport().setVerticalLocation(vOffset);
+    getViewport().setIgnoreScroll(false);
+    redraw(expose.x, expose.y, expose.width, expose.height, true);
+}
+
+/**
+ * Sets the given border on the LightweightSystem's root figure.
+ *
+ * @param   border  The new border
+ */
+public void setBorder(Border border) {
+    getLightweightSystem().getRootFigure().setBorder(border);
+}
+
+/**
+ * Sets the contents of the {@link Viewport}.
+ *
+ * @param figure the new contents
+ */
+public void setContents(IFigure figure) {
+    getViewport().setContents(figure);
+}
+
+/**
+ * @see dwt.widgets.Control#setFont(dwt.graphics.Font)
+ */
+public void setFont(Font font) {
+    this.font = font;
+    super.setFont(font);
+}
+
+/**
+ * Sets the horizontal scrollbar visibility.  Possible values are {@link #AUTOMATIC},
+ * {@link #ALWAYS}, and {@link #NEVER}.
+ *
+ * @param v the new visibility
+ */
+public void setHorizontalScrollBarVisibility(int v) {
+    hBarVisibility = v;
+}
+
+/**
+ * Sets both the horizontal and vertical scrollbar visibility to the given value.
+ * Possible values are {@link #AUTOMATIC}, {@link #ALWAYS}, and {@link #NEVER}.
+ * @param both the new visibility
+ */
+public void setScrollBarVisibility(int both) {
+    setHorizontalScrollBarVisibility(both);
+    setVerticalScrollBarVisibility(both);
+}
+
+/**
+ * Sets the vertical scrollbar visibility.  Possible values are {@link #AUTOMATIC},
+ * {@link #ALWAYS}, and {@link #NEVER}.
+ *
+ * @param v the new visibility
+ */
+public void setVerticalScrollBarVisibility(int v) {
+    vBarVisibility = v;
+}
+
+/**
+ * Sets the Viewport. The given Viewport must use "fake" scrolling. That is, it must be
+ * constructed using <code>new Viewport(true)</code>.
+ *
+ * @param vp the new viewport
+ */
+public void setViewport(Viewport vp) {
+    if (viewport !is null)
+        unhookViewport();
+    viewport = vp;
+    lws.setContents(viewport);
+    hookViewport();
+}
+
+private int verifyScrollBarOffset(RangeModel model, int value) {
+    value = Math.max(model.getMinimum(), value);
+    return Math.min(model.getMaximum() - model.getExtent(), value);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/FigureListener.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.FigureListener;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.IFigure;
+
+/**
+ * A listener interface for receiving notification that an IFigure has moved.
+ */
+public interface FigureListener {
+
+/**
+ * Called when the given IFigure has moved.
+ * @param source The IFigure that has moved.
+ */
+void figureMoved(IFigure source);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/FigureUtilities.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,399 @@
+/*******************************************************************************
+ * 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.FigureUtilities;
+
+import dwt.dwthelper.utils;
+
+import dwtx.dwtxhelper.Collection;
+
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.FontMetrics;
+import dwt.graphics.GC;
+import dwt.widgets.Shell;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.Shape;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Graphics;
+static import dwtx.draw2d.geometry.Point;
+/**
+ * Provides miscellaneous Figure operations.
+ */
+public class FigureUtilities {
+
+private static final float RGB_VALUE_MULTIPLIER = 0.6f;
+private static GC gc;
+private static Font appliedFont;
+private static FontMetrics metrics;
+private static Color ghostFillColor;
+
+static this(){
+    ghostFillColor = new Color(null, 31, 31, 31);
+}
+
+/**
+ * Returns a new Color the same as the passed color in a darker hue.
+ *
+ * @param color the color to darken
+ * @return the darkened color
+ * @since 2.0
+ */
+public static Color darker(Color color) {
+    return new Color(null,
+        cast(int)(color.getRed()   * RGB_VALUE_MULTIPLIER),
+        cast(int)(color.getGreen() * RGB_VALUE_MULTIPLIER),
+        cast(int)(color.getBlue()  * RGB_VALUE_MULTIPLIER));
+}
+
+/**
+ * Returns the FontMetrics associated with the passed Font.
+ *
+ * @param f the font
+ * @return the FontMetrics for the given font
+ * @see GC#getFontMetrics()
+ * @since 2.0
+ */
+public static FontMetrics getFontMetrics(Font f) {
+    setFont(f);
+    if (metrics is null)
+        metrics = getGC().getFontMetrics();
+    return metrics;
+}
+
+/**
+ * Returns the GC used for various utilities. Advanced graphics must not be switched on by
+ * clients using this GC.
+ * @deprecated do not mess with this GC
+ * @return the GC
+ */
+protected static GC getGC() {
+    if (gc is null) {
+        gc = new GC(new Shell());
+        appliedFont = gc.getFont();
+    }
+    return gc;
+}
+
+/**
+ * Returns the dimensions of the String <i>s</i> using the font <i>f</i>.  Tab expansion
+ * and carriage return processing are performed.
+ * @param s the string
+ * @param f the font
+ * @return the text's dimensions
+ * @see GC#textExtent(String)
+ */
+protected static dwt.graphics.Point.Point getTextDimension(String s, Font f) {
+    setFont(f);
+    return getGC().textExtent(s);
+}
+
+/**
+ * Returns the highest ancestor for the given figure
+ * @since 3.0
+ * @param figure a figure
+ * @return the root ancestor
+ */
+public static IFigure getRoot(IFigure figure) {
+    while (figure.getParent() !is null)
+        figure = figure.getParent();
+    return figure;
+}
+
+/**
+ * Returns the dimensions of the String <i>s</i> using the font <i>f</i>. No tab
+ * expansion or carriage return processing will be performed.
+ * @param s the string
+ * @param f the font
+ * @return the string's dimensions
+ * @see GC#stringExtent(java.lang.String)
+ */
+protected static dwt.graphics.Point.Point getStringDimension(String s, Font f) {
+    setFont(f);
+    return getGC().stringExtent(s);
+}
+
+/**
+ * Returns the Dimensions of the given text, converting newlines and tabs appropriately.
+ *
+ * @param text the text
+ * @param f the font
+ * @return the dimensions of the given text
+ * @since 2.0
+ */
+public static Dimension getTextExtents(String text, Font f) {
+    return new Dimension(getTextDimension(text, f));
+}
+
+/**
+ * Returns the Dimensions of <i>s</i> in Font <i>f</i>.
+ *
+ * @param s the string
+ * @param f the font
+ * @return the dimensions of the given string
+ * @since 2.0
+ */
+public static Dimension getStringExtents(String s, Font f) {
+    return new Dimension(getStringDimension(s, f));
+}
+
+/**
+ * Returns the Dimensions of the given text, converting newlines and tabs appropriately.
+ *
+ * @param s the string
+ * @param f the font
+ * @param result the Dimension that will contain the result of this calculation
+ * @since 2.0
+ */
+public static void getTextExtents(String s, Font f, Dimension result) {
+    dwt.graphics.Point.Point pt = getTextDimension(s, f);
+    result.width = pt.x;
+    result.height = pt.y;
+}
+
+/**
+ * Returns the width of <i>s</i> in Font <i>f</i>.
+ *
+ * @param s the string
+ * @param f the font
+ * @return the width
+ * @since 2.0
+ */
+public static int getTextWidth(String s, Font f) {
+    return getTextDimension(s, f).x;
+}
+
+/**
+ * Returns a Color the same as the passed color in a lighter hue.
+ *
+ * @param rgb the color
+ * @return the lighter color
+ * @since 2.0
+ */
+public static Color lighter(Color rgb) {
+    int r = rgb.getRed(),
+        g = rgb.getGreen(),
+        b = rgb.getBlue();
+
+    return new Color(null,
+        Math.max(2, Math.min(cast(int)(r / RGB_VALUE_MULTIPLIER), 255)),
+        Math.max(2, Math.min(cast(int)(g / RGB_VALUE_MULTIPLIER), 255)),
+        Math.max(2, Math.min(cast(int)(b / RGB_VALUE_MULTIPLIER), 255))
+    );
+}
+
+/**
+ * Produces a ghosting effect on the shape <i>s</i>.
+ *
+ * @param s the shape
+ * @return the ghosted shape
+ * @since 2.0
+ */
+public static Shape makeGhostShape(Shape s) {
+    s.setBackgroundColor(ghostFillColor);
+    s.setFillXOR(true);
+    s.setOutlineXOR(true);
+    return s;
+}
+
+/**
+ * Mixes the passed Colors and returns the resulting Color.
+ *
+ * @param c1 the first color
+ * @param c2 the second color
+ * @param weight the first color's weight from 0-1
+ * @return the new color
+ * @since 2.0
+ */
+public static Color mixColors(Color c1, Color c2, double weight) {
+    return new Color(null,
+            cast(int)(c1.getRed() * weight + c2.getRed() * (1 - weight)),
+            cast(int)(c1.getGreen() * weight + c2.getGreen() * (1 - weight)),
+            cast(int)(c1.getBlue() * weight + c2.getBlue() * (1 - weight)));
+}
+
+
+/**
+ * Mixes the passed Colors and returns the resulting Color.
+ *
+ * @param c1 the first color
+ * @param c2 the second color
+ * @return the new color
+ * @since 2.0
+ */
+public static Color mixColors(Color c1, Color c2) {
+    return new Color(null,
+        (c1.getRed() + c2.getRed()) / 2,
+        (c1.getGreen() + c2.getGreen()) / 2,
+        (c1.getBlue() + c2.getBlue()) / 2);
+}
+
+/**
+ * Paints a border with an etching effect, having a shadow of Color <i>shadow</i> and
+ * highlight of Color <i>highlight</i>.
+ *
+ * @param g the graphics object
+ * @param r the bounds of the border
+ * @param shadow the shadow color
+ * @param highlight the highlight color
+ * @since 2.0
+ */
+public static void paintEtchedBorder(Graphics g, Rectangle r,
+                                        Color shadow, Color highlight) {
+    int x = r.x,
+        y = r.y,
+        w = r.width,
+        h = r.height;
+
+    g.setLineStyle(Graphics.LINE_SOLID);
+    g.setLineWidth(1);
+    g.setXORMode(false);
+
+    w -= 2;
+    h -= 2;
+
+    g.setForegroundColor(shadow);
+    g.drawRectangle(x, y, w, h);
+
+    x++;
+    y++;
+    g.setForegroundColor(highlight);
+    g.drawRectangle(x, y, w, h);
+}
+
+/**
+ * Helper method to paint a grid.  Painting is optimized as it is restricted to the
+ * Graphics' clip.
+ *
+ * @param   g           The Graphics object to be used for painting
+ * @param   f           The figure in which the grid is to be painted
+ * @param   origin      Any point where the grid lines are expected to intersect
+ * @param   distanceX   Distance between vertical grid lines; if 0 or less, vertical grid
+ *                      lines will not be drawn
+ * @param   distanceY   Distance between horizontal grid lines; if 0 or less, horizontal
+ *                      grid lines will not be drawn
+ *
+ * @since 3.0
+ */
+public static void paintGrid(Graphics g, IFigure f,
+        dwtx.draw2d.geometry.Point.Point origin, int distanceX, int distanceY) {
+    Rectangle clip = g.getClip(Rectangle.SINGLETON);
+
+    if (distanceX > 0) {
+        if (origin.x >= clip.x)
+            while (origin.x - distanceX >= clip.x)
+                origin.x -= distanceX;
+        else
+            while (origin.x < clip.x)
+                origin.x += distanceX;
+        for (int i = origin.x; i < clip.x + clip.width; i += distanceX)
+            g.drawLine(i, clip.y, i, clip.y + clip.height);
+    }
+
+    if (distanceY > 0) {
+        if (origin.y >= clip.y)
+            while (origin.y - distanceY >= clip.y)
+                origin.y -= distanceY;
+        else
+            while (origin.y < clip.y)
+                origin.y += distanceY;
+        for (int i = origin.y; i < clip.y + clip.height; i += distanceY)
+            g.drawLine(clip.x, i, clip.x + clip.width, i);
+    }
+}
+
+/**
+ * Paints a border with an etching effect, having a shadow of a darker version of g's
+ * background color, and a highlight a lighter version of g's background color.
+ *
+ * @param g the graphics object
+ * @param r the bounds of the border
+ * @since 2.0
+ */
+public static void paintEtchedBorder(Graphics g, Rectangle r) {
+    Color rgb = g.getBackgroundColor(),
+        shadow = darker(rgb),
+        highlight = lighter(rgb);
+    paintEtchedBorder(g, r, shadow, highlight);
+}
+
+/**
+ * Sets Font to passed value.
+ *
+ * @param f the new font
+ * @since 2.0
+ */
+protected static void setFont(Font f) {
+    if (appliedFont is f || f.opEquals(appliedFont))
+        return;
+    getGC().setFont(f);
+    appliedFont = f;
+    metrics = null;
+}
+
+/**
+ * Returns the figure which is the ancestor of both figures, or <code>null</code>. A
+ * figure is an ancestor if it is the parent of another figure, or if it is the ancestor
+ * of that figure's parent. If one figure contains the other, <code>null</code> is
+ * returned.
+ * @since 3.1
+ * @param l left
+ * @param r right
+ * @return the common ancestor
+ */
+public static IFigure findCommonAncestor(IFigure l, IFigure r) {
+    if (l is r)
+        return l;
+    ArrayList left = new ArrayList();
+    ArrayList right = new ArrayList();
+    while (l !is null) {
+        left.add(cast(Object)l);
+        l = l.getParent();
+    }
+    while (r !is null) {
+        right.add(cast(Object)r);
+        r = r.getParent();
+    }
+    if (left.isEmpty() || right.isEmpty())
+        return null;
+
+    int il = left.size() - 1;
+    int ir = right.size() - 1;
+    do {
+        if (left.get(il) !is right.get(ir))
+                break;
+        il--;
+        ir--;
+    } while (il >= 0 && ir >= 0);
+
+    return cast(IFigure) left.get(il + 1);
+}
+
+/**
+ *  Returns <code>true</code> if the ancestor contains the descendant, or is the ancestor
+ *  of the descendant's parent.
+ * @param ancestor the ancestor
+ * @param descendant the descendant
+ * @return <code>true</code> if ancestor
+ * @since 3.2
+ */
+public static bool isAncestor(IFigure ancestor, IFigure descendant) {
+    while (descendant !is null) {
+        descendant = descendant.getParent();
+        if (descendant is ancestor)
+            return true;
+    }
+    return false;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/FlowLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,466 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.FlowLayout;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.geometry.Transposer;
+import dwtx.draw2d.AbstractHintLayout;
+import dwtx.draw2d.IFigure;
+
+/**
+ * Lays out children in rows or columns, wrapping when the current row/column is filled.
+ * The aligment and spacing of rows in the parent can be configured.  The aligment and
+ * spacing of children within a row can be configured.
+ */
+public class FlowLayout
+    : AbstractHintLayout
+{
+
+/** Constant to specify components to be aligned in the center */
+public static const int ALIGN_CENTER = 0;
+/** Constant to specify components to be aligned on the left/top */
+public static const int ALIGN_LEFTTOP = 1;
+/** Constant to specify components to be aligned on the right/bottom */
+public static const int ALIGN_RIGHTBOTTOM = 2;
+
+/** Constant to specify components should be layed out horizontally */
+public static const bool HORIZONTAL = true;
+/** Constant to specify components should be layed out vertically */
+public static const bool VERTICAL = false;
+
+/** The horizontal property. */
+protected bool horizontal = true;
+/**
+ * The property that determines whether leftover space at the end of a row/column should
+ * be filled by the last item in that row/column.
+ */
+protected bool fill = false;
+
+/** The transposer used in converting horizontal layout to vertical. */
+protected Transposer transposer;
+private void instanceInit(){
+    transposer = new Transposer();
+    transposer.setEnabled(!horizontal);
+}
+
+/** The alignment along the major axis. */
+protected int majorAlignment = ALIGN_LEFTTOP;
+/** The alignment along the minor axis. */
+protected int minorAlignment = ALIGN_LEFTTOP;
+/** The spacing along the minor axis. */
+protected int minorSpacing = 5;
+/** The spacing along the major axis. */
+protected int majorSpacing = 5;
+protected WorkingData data = null;
+
+/**
+ * Holds the necessary information for layout calculations.
+ */
+protected class WorkingData {
+    public int rowHeight, rowWidth, rowCount, rowX, rowY, maxWidth;
+    public Rectangle[] bounds;
+    public Rectangle area;
+    public IFigure row[];
+}
+
+/**
+ * Constructs a FlowLayout with horizontal orientation.
+ * @since 2.0
+ */
+public this() {
+    instanceInit();
+}
+
+/**
+ * Constructs a FlowLayout whose orientation is given in the input.
+ * @param isHorizontal <code>true</code> if the layout should be horizontal
+ * @since 2.0
+ */
+public this(bool isHorizontal) {
+    instanceInit();
+    setHorizontal(isHorizontal);
+}
+
+/**
+ * @see dwtx.draw2d.AbstractLayout#calculatePreferredSize(IFigure, int, int)
+ */
+protected Dimension calculatePreferredSize(IFigure container, int wHint, int hHint) {
+    // Subtract out the insets from the hints
+    if (wHint > -1)
+        wHint = Math.max(0, wHint - container.getInsets().getWidth());
+    if (hHint > -1)
+        hHint = Math.max(0, hHint - container.getInsets().getHeight());
+
+    // Figure out the new hint that we are interested in based on the orientation
+    // Ignore the other hint (by setting it to -1).  NOTE: The children of the
+    // parent figure will then be asked to ignore that hint as well.
+    int maxWidth;
+    if (isHorizontal()) {
+        maxWidth = wHint;
+        hHint = -1;
+    } else {
+        maxWidth = hHint;
+        wHint = -1;
+    }
+    if (maxWidth < 0) {
+        maxWidth = Integer.MAX_VALUE;
+    }
+
+    // The preferred dimension that is to be calculated and returned
+    Dimension prefSize = new Dimension();
+
+    List children = container.getChildren();
+    int width = 0;
+    int height = 0;
+    IFigure child;
+    Dimension childSize;
+
+    //Build the sizes for each row, and update prefSize accordingly
+    for (int i = 0; i < children.size(); i++) {
+        child = cast(IFigure)children.get(i);
+        childSize = transposer.t(getChildSize(child, wHint, hHint));
+        if (i is 0) {
+            width = childSize.width;
+            height = childSize.height;
+        } else if (width + childSize.width + getMinorSpacing() > maxWidth) {
+            // The current row is full, start a new row.
+            prefSize.height += height + getMajorSpacing();
+            prefSize.width = Math.max(prefSize.width, width);
+            width = childSize.width;
+            height = childSize.height;
+        } else {
+            // The current row can fit another child.
+            width += childSize.width + getMinorSpacing();
+            height = Math.max(height, childSize.height);
+        }
+    }
+
+    // Flush out the last row's data
+    prefSize.height += height;
+    prefSize.width = Math.max(prefSize.width, width);
+
+    // Transpose the dimension back, and compensate for the border.
+    prefSize = transposer.t(prefSize);
+    prefSize.width += container.getInsets().getWidth();
+    prefSize.height += container.getInsets().getHeight();
+    prefSize.union_(getBorderPreferredSize(container));
+
+    return prefSize;
+}
+
+/**
+ * Provides the given child's preferred size.
+ *
+ * @param child the Figure whose preferred size needs to be calculated
+ * @param wHint the width hint
+ * @param hHint the height hint
+ * @return the child's preferred size
+ */
+protected Dimension getChildSize(IFigure child, int wHint, int hHint) {
+    return child.getPreferredSize(wHint, hHint);
+}
+
+
+/**
+ * Returns the alignment used for an entire row/column.
+ * <P>
+ * Possible values are :
+ * <ul>
+ *   <li>{@link #ALIGN_CENTER}
+ *   <li>{@link #ALIGN_LEFTTOP}
+ *   <li>{@link #ALIGN_RIGHTBOTTOM}
+ * </ul>
+ *
+ * @return the major alignment
+ * @since 2.0
+ */
+public int getMajorAlignment() {
+    return majorAlignment;
+}
+
+/**
+ * Returns the spacing in pixels to be used between children in the direction parallel to
+ * the layout's orientation.
+ * @return the major spacing
+ */
+public int getMajorSpacing() {
+    return majorSpacing;
+}
+
+/**
+ * Returns the alignment used for children within a row/column.
+ * <P>
+ * Possible values are :
+ * <ul>
+ *   <li>{@link #ALIGN_CENTER}
+ *   <li>{@link #ALIGN_LEFTTOP}
+ *   <li>{@link #ALIGN_RIGHTBOTTOM}
+ * </ul>
+ *
+ * @return the minor alignment
+ * @since 2.0
+ */
+public int getMinorAlignment() {
+    return minorAlignment;
+}
+
+/**
+ * Returns the spacing to be used between children within a row/column.
+ * @return the minor spacing
+ */
+public int getMinorSpacing() {
+    return minorSpacing;
+}
+
+/**
+ * Initializes the state of row data, which is internal to the layout process.
+ */
+protected void initRow() {
+    data.rowX = 0;
+    data.rowHeight = 0;
+    data.rowWidth = 0;
+    data.rowCount = 0;
+}
+
+/**
+ * Initializes state data for laying out children, based on the Figure given as input.
+ *
+ * @param parent the parent figure
+ * @since 2.0
+ */
+protected void initVariables(IFigure parent) {
+    data.row = new IFigure[parent.getChildren().size()];
+    data.bounds = new Rectangle[data.row.length];
+    data.maxWidth = data.area.width;
+}
+
+/**
+ * Returns <code>true</code> if the orientation of the layout is horizontal.
+ *
+ * @return <code>true</code> if the orientation of the layout is horizontal
+ * @since 2.0
+ */
+public bool isHorizontal() {
+    return horizontal;
+}
+
+/**
+ * @see dwtx.draw2d.AbstractHintLayout#isSensitiveHorizontally(IFigure)
+ */
+protected bool isSensitiveHorizontally(IFigure parent) {
+    return isHorizontal();
+}
+
+/**
+ * @see dwtx.draw2d.AbstractHintLayout#isSensitiveVertically(IFigure)
+ */
+protected bool isSensitiveVertically(IFigure parent) {
+    return !isHorizontal();
+}
+
+/**
+ * @see dwtx.draw2d.LayoutManager#layout(IFigure)
+ */
+public void layout(IFigure parent) {
+    data = new WorkingData();
+    Rectangle relativeArea = parent.getClientArea();
+    data.area = transposer.t(relativeArea);
+
+    Iterator iterator = parent.getChildren().iterator();
+    int dx;
+
+    //Calculate the hints to be passed to children
+    int wHint = -1;
+    int hHint = -1;
+    if (isHorizontal())
+        wHint = parent.getClientArea().width;
+    else
+        hHint = parent.getClientArea().height;
+
+    initVariables(parent);
+    initRow();
+    int i = 0;
+    while (iterator.hasNext()) {
+        IFigure f = cast(IFigure)iterator.next();
+        Dimension pref = transposer.t(getChildSize(f, wHint, hHint));
+        Rectangle r = new Rectangle(0, 0, pref.width, pref.height);
+
+        if (data.rowCount > 0) {
+            if (data.rowWidth + pref.width > data.maxWidth)
+                layoutRow(parent);
+        }
+        r.x = data.rowX;
+        r.y = data.rowY;
+        dx = r.width + getMinorSpacing();
+        data.rowX += dx;
+        data.rowWidth += dx;
+        data.rowHeight = Math.max(data.rowHeight, r.height);
+        data.row [data.rowCount] = f;
+        data.bounds[data.rowCount] = r;
+        data.rowCount++;
+        i++;
+    }
+    if (data.rowCount !is 0)
+        layoutRow(parent);
+    data = null;
+}
+
+/**
+ * Layouts one row of components. This is done based on the layout's orientation, minor
+ * alignment and major alignment.
+ *
+ * @param parent the parent figure
+ * @since 2.0
+ */
+protected void layoutRow(IFigure parent) {
+    int majorAdjustment = 0;
+    int minorAdjustment = 0;
+    int correctMajorAlignment = majorAlignment;
+    int correctMinorAlignment = minorAlignment;
+
+    majorAdjustment = data.area.width - data.rowWidth + getMinorSpacing();
+
+    switch (correctMajorAlignment) {
+        case ALIGN_LEFTTOP:
+            majorAdjustment = 0;
+            break;
+        case ALIGN_CENTER:
+            majorAdjustment /= 2;
+            break;
+        case ALIGN_RIGHTBOTTOM:
+            break;
+    }
+
+    for (int j = 0; j < data.rowCount; j++) {
+        if (fill) {
+            data.bounds[j].height = data.rowHeight;
+        } else {
+            minorAdjustment = data.rowHeight - data.bounds[j].height;
+            switch (correctMinorAlignment) {
+                case ALIGN_LEFTTOP:
+                    minorAdjustment = 0;
+                    break;
+                case ALIGN_CENTER:
+                    minorAdjustment /= 2;
+                break;
+                case ALIGN_RIGHTBOTTOM:
+                    break;
+            }
+            data.bounds[j].y += minorAdjustment;
+        }
+        data.bounds[j].x += majorAdjustment;
+
+        setBoundsOfChild(parent, data.row[j], transposer.t(data.bounds[j]));
+    }
+    data.rowY += getMajorSpacing() + data.rowHeight;
+    initRow();
+}
+
+/**
+ * Sets the given bounds for the child figure input.
+ *
+ * @param parent the parent figure
+ * @param child the child figure
+ * @param bounds the size of the child to be set
+ * @since 2.0
+ */
+protected void setBoundsOfChild(IFigure parent, IFigure child, Rectangle bounds) {
+    parent.getClientArea(Rectangle.SINGLETON);
+    bounds.translate(Rectangle.SINGLETON.x, Rectangle.SINGLETON.y);
+    child.setBounds(bounds);
+}
+
+/**
+ * Sets flag based on layout orientation. If in horizontal orientation, all figures will
+ * have the same height. If in vertical orientation, all figures will have the same width.
+ *
+ * @param value fill state desired
+ * @since 2.0
+ */
+public void setStretchMinorAxis(bool value) {
+    fill = value;
+}
+
+/**
+ * Sets the orientation of the layout.
+ *
+ * @param flag <code>true</code> if this layout should be horizontal
+ * @since 2.0
+ */
+public void setHorizontal(bool flag) {
+    if (horizontal is flag) return;
+    invalidate();
+    horizontal = flag;
+    transposer.setEnabled(!horizontal);
+}
+
+/**
+ * Sets the alignment for an entire row/column within the parent figure.
+ * <P>
+ * Possible values are :
+ * <ul>
+ *   <li>{@link #ALIGN_CENTER}
+ *   <li>{@link #ALIGN_LEFTTOP}
+ *   <li>{@link #ALIGN_RIGHTBOTTOM}
+ * </ul>
+ *
+ * @param align the major alignment
+ * @since 2.0
+ */
+public void setMajorAlignment(int align_) {
+    majorAlignment = align_;
+}
+
+/**
+ * Sets the spacing in pixels to be used between children in the direction parallel to the
+ * layout's orientation.
+ *
+ * @param n the major spacing
+ * @since 2.0
+ */
+public void setMajorSpacing(int n) {
+    majorSpacing = n;
+}
+
+/**
+ * Sets the alignment to be used within a row/column.
+ * <P>
+ * Possible values are :
+ * <ul>
+ *   <li>{@link #ALIGN_CENTER}
+ *   <li>{@link #ALIGN_LEFTTOP}
+ *   <li>{@link #ALIGN_RIGHTBOTTOM}
+ * </ul>
+ *
+ * @param align the minor alignment
+ * @since 2.0
+ */
+public void setMinorAlignment(int align_) {
+    minorAlignment = align_;
+}
+
+/**
+ * Sets the spacing to be used between children within a row/column.
+ *
+ * @param n the minor spacing
+ * @since 2.0
+ */
+public void setMinorSpacing(int n) {
+    minorSpacing = n;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/FocusBorder.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.FocusBorder;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.AbstractBorder;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.ColorConstants;
+
+/**
+ * A Border that looks like the system's focus rectangle.
+ */
+public class FocusBorder
+    : AbstractBorder
+{
+
+/**
+ * Constructs a new FocusBorder.
+ */
+public this() { }
+
+/**
+ * @see dwtx.draw2d.Border#getInsets(IFigure)
+ */
+public Insets getInsets(IFigure figure) {
+    return new Insets(1);
+}
+
+/**
+ * @see dwtx.draw2d.Border#isOpaque()
+ */
+public bool isOpaque() {
+    return true;
+}
+
+/**
+ * Paints a focus rectangle.
+ * @see dwtx.draw2d.Border#paint(IFigure, Graphics, Insets)
+ */
+public void paint(IFigure figure, Graphics graphics, Insets insets) {
+    tempRect.setBounds(getPaintRectangle(figure, insets));
+    tempRect.width--;
+    tempRect.height--;
+    graphics.setForegroundColor(ColorConstants.black);
+    graphics.setBackgroundColor(ColorConstants.white);
+    graphics.drawFocus(tempRect);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/FocusEvent.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.FocusEvent;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.IFigure;
+
+/**
+ * An event that occurs when an {@link dwtx.draw2d.IFigure} gains or loses focus.
+ */
+public class FocusEvent {
+
+/** The figure losing focus */
+public IFigure loser;
+/** The figure gaining focus */
+public IFigure gainer;
+
+/**
+ * Constructs a new FocusEvent.
+ * @param loser the figure losing focus
+ * @param gainer the figure gaining focus
+ */
+public this(IFigure loser, IFigure gainer) {
+    this.loser = loser;
+    this.gainer = gainer;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/FocusListener.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.FocusListener;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.FocusEvent;
+
+/**
+ * A listener interface for receiving {@link FocusEvent FocusEvents}.
+ */
+public interface FocusListener {
+
+/**
+ * Called when the listened to object has gained focus.
+ * @param fe The FocusEvent object
+ */
+void focusGained(FocusEvent fe);
+
+/**
+ * Called when the listened to object has lost focus.
+ * @param fe The FocusEvent object
+ */
+void focusLost(FocusEvent fe);
+
+/**
+ * An empty implementation of FocusListener for convenience.
+ */
+public class Stub
+    : FocusListener
+{
+    /**
+     * @see dwtx.draw2d.FocusListener#focusGained(FocusEvent)
+     */
+    public void focusGained(FocusEvent fe) { }
+    /**
+     * @see dwtx.draw2d.FocusListener#focusLost(FocusEvent)
+     */
+    public void focusLost(FocusEvent fe) { }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/FocusTraverseManager.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.FocusTraverseManager;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.draw2d.IFigure;
+
+/**
+ * This class is a helper to the {@link SWTEventDispatcher}. It handles the task of
+ * determining which Figure will gain focus upon a tab/shift-tab. It also keeps track of
+ * the Figure with current focus.
+ * <p>
+ * Note: When a Canvas with a {@link LightweightSystem} gains focus, it gives focus to the
+ * child Figure who had focus when this Canvas lost focus. If the canvas is gaining focus
+ * for the first time, focus is given to its first child Figure.
+ */
+public class FocusTraverseManager {
+
+IFigure currentFocusOwner;
+
+/**
+ * Default constructor.
+ */
+public this() { }
+
+private IFigure findDeepestRightmostChildOf(IFigure fig) {
+    while (fig.getChildren().size() !is 0) {
+        fig = cast(IFigure)fig.getChildren().get(fig.getChildren().size() - 1);
+    }
+    return fig;
+}
+
+/**
+ * Returns the IFigure that will receive focus upon a 'tab' traverse event.
+ *
+ * @param root the {@link LightweightSystem LightweightSystem's} root figure
+ * @param prevFocus the IFigure who currently owns focus
+ * @return the next focusable figure
+ */
+public IFigure getNextFocusableFigure(IFigure root, IFigure prevFocus) {
+    bool found = false;
+    IFigure nextFocus = prevFocus;
+
+    /*
+     * If no Figure currently has focus, apply focus to root's first focusable child.
+     */
+    if (prevFocus is null) {
+        if (root.getChildren().size() !is 0) {
+            nextFocus = (cast(IFigure)root.getChildren().get(0));
+            if (isFocusEligible(nextFocus))
+                return nextFocus;
+        } else
+            return null;
+    }
+
+    int siblingPos = nextFocus.getParent().getChildren().indexOf(cast(Object)nextFocus);
+    while (!found) {
+        IFigure parent = nextFocus.getParent();
+
+        /*
+         * Figure traversal is implemented using the pre-order left to right
+         * tree traversal algorithm.
+         *
+         * If the focused sibling has children, traverse to its leftmost child.
+         * If the focused sibling has no children, traverse to the sibling
+         * to its right.
+         * If there is no sibling to the right, go up the tree until a node
+         * with un-traversed siblings is found.
+         */
+        List siblings = parent.getChildren();
+
+        if (nextFocus.getChildren().size() !is 0) {
+            nextFocus = cast(IFigure)nextFocus.getChildren().get(0);
+            siblingPos = 0;
+            if (isFocusEligible(nextFocus))
+                found = true;
+        } else if (siblingPos < siblings.size() - 1) {
+            nextFocus = (cast(IFigure)(siblings.get(++siblingPos)));
+            if (isFocusEligible(nextFocus))
+                found = true;
+        } else {
+            bool untraversedSiblingFound = false;
+            while (!untraversedSiblingFound) {
+                IFigure p = nextFocus.getParent();
+                IFigure gp = p.getParent();
+
+                if (gp !is null) {
+                    int parentSiblingCount = gp.getChildren().size();
+                    int parentIndex = gp.getChildren().indexOf(cast(Object)p);
+                    if (parentIndex < parentSiblingCount - 1) {
+                        nextFocus = (cast(IFigure)p.getParent()
+                                .getChildren().get(parentIndex + 1));
+                        siblingPos = parentIndex + 1;
+                        untraversedSiblingFound = true;
+                        if (isFocusEligible(nextFocus))
+                            found = true;
+                    } else
+                        nextFocus = p;
+                } else {
+                    nextFocus = null;
+                    untraversedSiblingFound = true;
+                    found = true;
+                }
+            }
+        }
+    }
+    return nextFocus;
+}
+
+/**
+ * Returns the IFigure that will receive focus upon a 'shift-tab' traverse event.
+ *
+ * @param root The {@link LightweightSystem LightweightSystem's} root figure
+ * @param prevFocus The IFigure who currently owns focus
+ * @return the previous focusable figure
+ */
+public IFigure getPreviousFocusableFigure(IFigure root, IFigure prevFocus) {
+    if (prevFocus is null)
+        return null;
+
+    bool found = false;
+    IFigure nextFocus = prevFocus;
+    while (!found) {
+        IFigure parent = nextFocus.getParent();
+
+        /*
+         * At root, return null to indicate traversal
+         * is complete.
+         */
+        if (parent is null)
+            return null;
+
+        List siblings = parent.getChildren();
+        int siblingPos = siblings.indexOf(cast(Object)nextFocus);
+
+        /*
+         * Figure traversal is implemented using the post-order right to left
+         * tree traversal algorithm.
+         *
+         * Find the rightmost child.
+         * If this child is focusable, return it
+         * If not focusable, traverse to its sibling and repeat.
+         * If there is no sibling, traverse its parent.
+         */
+        if (siblingPos !is 0) {
+            IFigure child =
+                    findDeepestRightmostChildOf(cast(IFigure)siblings.get(siblingPos - 1));
+            if (isFocusEligible(child)) {
+                found = true;
+                nextFocus = child;
+            } else if ((cast(Object)child).opEquals(cast(Object)nextFocus)) {
+                if (isFocusEligible(nextFocus))
+                    found = true;
+            } else
+                nextFocus = child;
+        } else {
+            nextFocus = parent;
+                if (isFocusEligible(nextFocus))
+                    found = true;
+        }
+    }
+    return nextFocus;
+}
+
+/**
+ * @return the figure that currently has focus
+ */
+public IFigure getCurrentFocusOwner() {
+    return currentFocusOwner;
+}
+
+private bool isFocusEligible(IFigure fig) {
+    return (fig !is null && fig.isFocusTraversable() && fig.isShowing());
+}
+
+/**
+ * Sets the currently focused figure.
+ * @param fig the figure to get focus
+ */
+public void setCurrentFocusOwner(IFigure fig) {
+    currentFocusOwner = fig;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/FrameBorder.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.FrameBorder;
+
+import dwt.dwthelper.utils;
+
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwtx.draw2d.CompoundBorder;
+import dwtx.draw2d.LabeledBorder;
+import dwtx.draw2d.SchemeBorder;
+import dwtx.draw2d.ColorConstants;
+import dwtx.draw2d.TitleBarBorder;
+
+
+/**
+ * Provides for a frame-like border which contains a title bar for holding the title of a
+ * Figure.
+ */
+public class FrameBorder
+    : CompoundBorder
+    , LabeledBorder
+{
+
+/**
+ * The border scheme that determines the border highlight and shadow colors, as well as
+ * the border width (3).
+ */
+protected static const SchemeBorder.Scheme
+    SCHEME_FRAME;
+static this(){
+    SCHEME_FRAME = new SchemeBorder.Scheme(
+        [
+                        ColorConstants.button,
+                        ColorConstants.buttonLightest,
+                        ColorConstants.button
+                     ],
+        [
+                        ColorConstants.buttonDarkest,
+                        ColorConstants.buttonDarker,
+                        ColorConstants.button
+                     ]
+    );
+}
+
+private void instanceInit(){
+    createBorders();
+}
+
+/**
+ * Constructs a FrameBorder with its label set to the name of the {@link TitleBarBorder}
+ * class.
+ *
+ * @since 2.0
+ */
+public this() {
+    instanceInit();
+}
+
+/**
+ * Constructs a FrameBorder with the title set to the passed String.
+ *
+ * @param label  label or title of the frame.
+ * @since 2.0
+ */
+public this(String label) {
+    instanceInit();
+    setLabel(label);
+}
+
+/**
+ * Creates the necessary borders for this FrameBorder. The inner border is a
+ * {@link TitleBarBorder}. The outer border is a {@link SchemeBorder}.
+ *
+ * @since 2.0
+ */
+protected void createBorders() {
+    inner = new TitleBarBorder();
+    outer = new SchemeBorder(SCHEME_FRAME);
+}
+
+/**
+ * Returns the inner border of this FrameBorder, which contains the label for the
+ * FrameBorder.
+ *
+ * @return  the border holding the label.
+ * @since 2.0
+ */
+protected LabeledBorder getLabeledBorder() {
+    return cast(LabeledBorder)inner;
+}
+
+/**
+ * @return the label for this border
+ */
+public String getLabel() {
+    return getLabeledBorder().getLabel();
+}
+
+/**
+ * Sets the label for this border.
+ * @param label the label
+ */
+public void setLabel(String label) {
+    getLabeledBorder().setLabel(label);
+}
+
+/**
+ * Sets the font for this border's label.
+ * @param font the font
+ */
+public void setFont(Font font) {
+    getLabeledBorder().setFont(font);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/FreeformFigure.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.FreeformFigure;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.FreeformListener;
+
+
+/**
+ * A figure that allows its children to extend into negative coordinates.  This figure
+ * must be placed in a {@link dwtx.draw2d.FreeformViewport}.  Also, you shouldn't
+ * call {@link IFigure#setBounds(Rectangle)} on this figure.  The bounds will be
+ * calculated based on the extent of its children.  A FreeformFigure's bounds will be the
+ * smallest rectangle that will contain all of its children.
+ */
+public interface FreeformFigure
+    : IFigure
+{
+
+/**
+ * Adds a FreeformListener to this FreeformFigure.
+ * @param listener the listener
+ */
+void addFreeformListener(FreeformListener listener);
+
+/**
+ * Notifies listeners that the freeform extent (i.e. bounds) has changed.
+ */
+void fireExtentChanged();
+
+/**
+ * Returns the freeform extent, essentially the bounds of the FreeformFigure.  This is
+ * based on the extent of its children.
+ * @return the freeform extent
+ */
+Rectangle getFreeformExtent();
+
+/**
+ * Removes the given listener from this FreeformFigure.
+ * @param listener the listener
+ */
+void removeFreeformListener(FreeformListener listener);
+
+/**
+ * Sets the freeform bounds of this FreeformFigure.
+ * @param bounds the new freeform bounds
+ */
+void setFreeformBounds(Rectangle bounds);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/FreeformHelper.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.FreeformHelper;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.FreeformListener;
+import dwtx.draw2d.FreeformFigure;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.FigureListener;
+
+class FreeformHelper
+    : FreeformListener
+{
+
+class ChildTracker : FigureListener {
+    public void figureMoved(IFigure source) {
+        invalidate();
+    }
+}
+
+private FreeformFigure host;
+private Rectangle freeformExtent;
+private FigureListener figureListener;
+
+private void instanceInit(){
+    figureListener = new ChildTracker();
+}
+this(FreeformFigure host) {
+    instanceInit();
+    this.host = host;
+}
+
+public Rectangle getFreeformExtent() {
+    if (freeformExtent !is null)
+        return freeformExtent;
+    Rectangle r;
+    List children = host.getChildren();
+    for (int i = 0; i < children.size(); i++) {
+        IFigure child = cast(IFigure)children.get(i);
+        if (null !is cast(FreeformFigure) child )
+            r = (cast(FreeformFigure) child).getFreeformExtent();
+        else
+            r = child.getBounds();
+        if (freeformExtent is null)
+            freeformExtent = r.getCopy();
+        else
+            freeformExtent.union_(r);
+    }
+    Insets insets = host.getInsets();
+    if (freeformExtent is null)
+        freeformExtent = new Rectangle(0, 0, insets.getWidth(), insets.getHeight());
+    else {
+        host.translateToParent(freeformExtent);
+        freeformExtent.expand(insets);
+    }
+//  System.out.println("New extent calculated for " + host + " = " + freeformExtent);
+    return freeformExtent;
+}
+
+public void hookChild(IFigure child) {
+    invalidate();
+    if (null !is cast(FreeformFigure)child )
+        (cast(FreeformFigure)child).addFreeformListener(this);
+    else
+        child.addFigureListener(figureListener);
+}
+
+void invalidate() {
+    freeformExtent = null;
+    host.fireExtentChanged();
+    if (host.getParent() !is null)
+        host.getParent().revalidate();
+    else
+        host.revalidate();
+}
+
+public void notifyFreeformExtentChanged() {
+    //A childs freeform extent has changed, therefore this extent must be recalculated
+    invalidate();
+}
+
+public void setFreeformBounds(Rectangle bounds) {
+    host.setBounds(bounds);
+    bounds = bounds.getCopy();
+    host.translateFromParent(bounds);
+    List children = host.getChildren();
+    for (int i = 0; i < children.size(); i++) {
+        IFigure child = cast(IFigure)children.get(i);
+        if (null !is cast(FreeformFigure)child )
+            (cast(FreeformFigure) child).setFreeformBounds(bounds);
+    }
+}
+
+public void unhookChild(IFigure child) {
+    invalidate();
+    if (null !is cast(FreeformFigure)child )
+        (cast(FreeformFigure)child).removeFreeformListener(this);
+    else
+        child.removeFigureListener(figureListener);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/FreeformLayer.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.FreeformLayer;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Rectangle;
+
+import dwtx.draw2d.Layer;
+import dwtx.draw2d.FreeformFigure;
+import dwtx.draw2d.FreeformListener;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.FreeformHelper;
+
+/**
+ * A Layer that can extend in all 4 directions.
+ */
+public class FreeformLayer
+    : Layer
+    , FreeformFigure
+{
+
+private FreeformHelper helper;
+
+private void instanceInit(){
+    helper = new FreeformHelper(this);
+}
+
+this(){
+    helper = new FreeformHelper(this);
+}
+/**
+ * @see IFigure#add(IFigure, Object, int)
+ */
+public void add(IFigure child, Object constraint, int index) {
+    super.add(child, constraint, index);
+    helper.hookChild(child);
+}
+
+/**
+ * @see FreeformFigure#addFreeformListener(FreeformListener)
+ */
+public void addFreeformListener(FreeformListener listener) {
+    addListener(FreeformListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * @see FreeformFigure#fireExtentChanged()
+ */
+public void fireExtentChanged() {
+    Iterator iter = getListeners(FreeformListener.classinfo);
+    while (iter.hasNext())
+        (cast(FreeformListener)iter.next())
+            .notifyFreeformExtentChanged();
+}
+
+/**
+ * Overrides to do nothing.
+ * @see Figure#fireMoved()
+ */
+protected void fireMoved() { }
+
+/**
+ * @see FreeformFigure#getFreeformExtent()
+ */
+public Rectangle getFreeformExtent() {
+    return helper.getFreeformExtent();
+}
+
+/**
+ * @see Figure#primTranslate(int, int)
+ */
+public void primTranslate(int dx, int dy) {
+    bounds.x += dx;
+    bounds.y += dy;
+}
+
+/**
+ * @see IFigure#remove(IFigure)
+ */
+public void remove(IFigure child) {
+    helper.unhookChild(child);
+    super.remove(child);
+}
+
+/**
+ * @see FreeformFigure#removeFreeformListener(FreeformListener)
+ */
+public void removeFreeformListener(FreeformListener listener) {
+    removeListener(FreeformListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * @see FreeformFigure#setFreeformBounds(Rectangle)
+ */
+public void setFreeformBounds(Rectangle bounds) {
+    helper.setFreeformBounds(bounds);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/FreeformLayeredPane.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.FreeformLayeredPane;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.LayeredPane;
+import dwtx.draw2d.FreeformFigure;
+import dwtx.draw2d.FreeformHelper;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.FreeformListener;
+
+/**
+ * A LayeredPane that contains {@link dwtx.draw2d.FreeformLayer FreeformLayers}.
+ */
+public class FreeformLayeredPane
+    : LayeredPane
+    , FreeformFigure
+{
+private FreeformHelper helper;
+
+/**
+ * Constructs a new FreeformLayeredPane.
+ */
+public this() {
+    helper = new FreeformHelper(this);
+    setLayoutManager(null);
+}
+
+/**
+ * @see IFigure#add(IFigure, Object, int)
+ */
+public void add(IFigure child, Object constraint, int index) {
+    super.add(child, constraint, index);
+    helper.hookChild(child);
+}
+
+/**
+ * @see FreeformFigure#addFreeformListener(FreeformListener)
+ */
+public void addFreeformListener(FreeformListener listener) {
+    addListener(FreeformListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * @see FreeformFigure#fireExtentChanged()
+ */
+public void fireExtentChanged() {
+    Iterator iter = getListeners(FreeformListener.classinfo);
+    while (iter.hasNext())
+        (cast(FreeformListener)iter.next())
+            .notifyFreeformExtentChanged();
+}
+
+/**
+ * Overrides to do nothing.
+ * @see Figure#fireMoved()
+ */
+protected void fireMoved() { }
+
+/**
+ * Returns the FreeformHelper.
+ * @return the FreeformHelper
+ */
+protected FreeformHelper getFreeformHelper() {
+    return helper;
+}
+
+/**
+ * @see FreeformFigure#getFreeformExtent()
+ */
+public Rectangle getFreeformExtent() {
+    return helper.getFreeformExtent();
+}
+
+/**
+ * @see Figure#primTranslate(int, int)
+ */
+protected void primTranslate(int dx, int dy) {
+    bounds.x += dx;
+    bounds.y += dy;
+}
+
+/**
+ * @see IFigure#remove(IFigure)
+ */
+public void remove(IFigure child) {
+    helper.unhookChild(child);
+    super.remove(child);
+}
+
+/**
+ * @see FreeformFigure#removeFreeformListener(FreeformListener)
+ */
+public void removeFreeformListener(FreeformListener listener) {
+    removeListener(FreeformListener.classinfo, cast(Object)listener);
+}
+
+/**
+ * @see FreeformFigure#setFreeformBounds(Rectangle)
+ */
+public void setFreeformBounds(Rectangle bounds) {
+    helper.setFreeformBounds(bounds);
+}
+
+/**
+ * Calls {@link Figure#fireMoved() super.fireMoved()}.
+ */
+protected void superFireMoved() {
+    super.fireMoved();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/FreeformLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.FreeformLayout;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.XYLayout;
+import dwtx.draw2d.IFigure;
+
+/**
+ * A layout for {@link dwtx.draw2d.FreeformFigure FreeformFigures}.
+ */
+public class FreeformLayout
+    : XYLayout
+{
+
+/**
+ * Returns the point (0,0) as the origin.
+ * @see XYLayout#getOrigin(IFigure)
+ */
+public Point getOrigin(IFigure figure) {
+    return new Point();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/FreeformListener.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.FreeformListener;
+
+import dwt.dwthelper.utils;
+
+/**
+ * Listens for extent changes in {@link dwtx.draw2d.FreeformFigure FreeformFigures}.
+ * Created on :Oct 4, 2002
+ * @author hudsonr
+ * @since 2.0
+ */
+public interface FreeformListener {
+
+/**
+ * Called when the extent of the FreeformFigure has changed.
+ */
+void notifyFreeformExtentChanged();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/FreeformViewport.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.FreeformViewport;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.Viewport;
+import dwtx.draw2d.ViewportLayout;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.FreeformFigure;
+
+/**
+ * A viewport for {@link dwtx.draw2d.FreeformFigure FreeformFigures}.
+ * FreeformFigures can only reside in this type of viewport.
+ */
+public class FreeformViewport
+    : Viewport
+{
+
+class FreeformViewportLayout
+    : ViewportLayout
+{
+    protected Dimension calculatePreferredSize(IFigure parent, int wHint, int hHint) {
+        getContents().validate();
+        wHint = Math.max(0, wHint);
+        hHint = Math.max(0, hHint);
+        return (cast(FreeformFigure)getContents())
+            .getFreeformExtent()
+            .getExpanded(getInsets())
+            .union_(0, 0)
+            .union_(wHint - 1, hHint - 1)
+            .getSize();
+    }
+
+    protected bool isSensitiveHorizontally(IFigure parent) {
+        return true;
+    }
+
+    protected bool isSensitiveVertically(IFigure parent) {
+        return true;
+    }
+
+
+    public void layout(IFigure figure) {
+        //Do nothing, contents updates itself.
+    }
+}
+
+/**
+ * Constructs a new FreeformViewport.  This viewport must use graphics translation to
+ * scroll the FreeformFigures inside of it.
+ */
+public this() {
+    super(true); //Must use graphics translate to scroll freeforms.
+    setLayoutManager(new FreeformViewportLayout());
+}
+
+/**
+ * Readjusts the scrollbars.  In doing so, it gets the freeform extent of the contents and
+ * unions this rectangle with this viewport's client area, then sets the contents freeform
+ * bounds to be this unioned rectangle.  Then proceeds to set the scrollbar values based
+ * on this new information.
+ * @see Viewport#readjustScrollBars()
+ */
+protected void readjustScrollBars() {
+    if (getContents() is null)
+        return;
+    if (!( null !is cast(FreeformFigure)getContents() ))
+        return;
+    FreeformFigure ff = cast(FreeformFigure)getContents();
+    Rectangle clientArea = getClientArea();
+    Rectangle bounds = ff.getFreeformExtent().getCopy();
+    bounds.union_(0, 0, clientArea.width, clientArea.height);
+    ff.setFreeformBounds(bounds);
+
+    getVerticalRangeModel().setAll(bounds.y, clientArea.height, bounds.bottom());
+    getHorizontalRangeModel().setAll(bounds.x, clientArea.width, bounds.right());
+}
+
+/**
+ * Returns <code>true</code>.
+ * @see Figure#useLocalCoordinates()
+ */
+protected bool useLocalCoordinates() {
+    return true;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Graphics.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,934 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.Graphics;
+
+import dwt.dwthelper.utils;
+
+
+
+import dwt.DWT;
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.FontMetrics;
+import dwt.graphics.GC;
+import dwt.graphics.Image;
+import dwt.graphics.Path;
+import dwt.graphics.Pattern;
+import dwt.graphics.TextLayout;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.geometry.Rectangle;
+
+/**
+ * The Graphics class allows you to draw to a surface.  The drawXxx() methods that pertain
+ * to shapes draw an outline of the shape, whereas the fillXxx() methods fill in the shape.
+ * Also provides for drawing text, lines and images.
+ */
+public abstract class Graphics {
+
+/**
+ * @see DWT#LINE_CUSTOM
+ */
+public static final int LINE_CUSTOM = DWT.LINE_CUSTOM;
+
+/**
+ * @see DWT#LINE_DASH
+ */
+public static final int LINE_DASH = DWT.LINE_DASH;
+
+/**
+ * @see DWT#LINE_DASHDOT
+ */
+public static final int LINE_DASHDOT = DWT.LINE_DASHDOT;
+
+/**
+ * @see DWT#LINE_DASHDOTDOT
+ */
+public static final int LINE_DASHDOTDOT = DWT.LINE_DASHDOTDOT;
+
+/**
+ * @see DWT#LINE_DOT
+ */
+public static final int LINE_DOT = DWT.LINE_DOT;
+
+/**
+ * @see DWT#LINE_SOLID
+ */
+public static final int LINE_SOLID = DWT.LINE_SOLID;
+
+/**
+ * Sets the clip region to the given rectangle.  Anything outside this rectangle will not
+ * be drawn.
+ * @param r the clip rectangle
+ */
+public abstract void clipRect(Rectangle r);
+
+/**
+ * Disposes this object, releasing any resources.
+ */
+public abstract void dispose();
+
+/**
+ * Draws the outline of an arc located at (x,y) with width <i>w</i> and height <i>h</i>.
+ * The starting angle of the arc (specified in degrees) is <i>offset</i> and <i>length</i>
+ * is the arc's angle (specified in degrees).
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param w the width
+ * @param h the height
+ * @param offset the start angle
+ * @param length the length of the arc
+ */
+public abstract void drawArc(int x, int y, int w, int h, int offset, int length);
+
+/**
+ * @see #drawArc(int, int, int, int, int, int)
+ */
+public final void drawArc(Rectangle r, int offset, int length) {
+    drawArc(r.x, r.y, r.width, r.height, offset, length);
+}
+
+/**
+ * Draws a focus rectangle.
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param w the width
+ * @param h the height
+ */
+public abstract void drawFocus(int x, int y, int w, int h);
+
+/**
+ * @see #drawFocus(int, int, int, int)
+ */
+public final void drawFocus(Rectangle r) {
+    drawFocus(r.x, r.y, r.width, r.height);
+}
+
+/**
+ * Draws the given Image at the location (x,y).
+ * @param srcImage the Image
+ * @param x the x coordinate
+ * @param y the y coordinate
+ */
+public abstract void drawImage(Image srcImage, int x, int y);
+
+/**
+ * Draws a rectangular section of the given Image to the specified rectangular reagion on
+ * the canvas.  The section of the image bounded by the rectangle (x1,y1,w1,h1) is copied
+ * to the section of the canvas bounded by the rectangle (x2,y2,w2,h2).  If these two
+ * sizes are different, scaling will occur.
+ *
+ * @param srcImage the image
+ * @param x1 the x coordinate of the source
+ * @param y1 the y coordinate of the source
+ * @param w1 the width of the source
+ * @param h1 the height of the source
+ * @param x2 the x coordinate of the destination
+ * @param y2 the y coordinate of the destination
+ * @param w2 the width of the destination
+ * @param h2 the height of the destination
+ */
+public abstract void drawImage(Image srcImage, int x1, int y1, int w1, int h1,
+                                                    int x2, int y2, int w2, int h2);
+
+/**
+ * Draws the given image at a point.
+ * @param image the image to draw
+ * @param p where to draw the image
+ * @see #drawImage(Image, int, int)
+ */
+public final void drawImage(Image image, Point p) {
+    drawImage(image, p.x, p.y);
+}
+
+/**
+ * @see #drawImage(Image, int, int, int, int, int, int, int, int)
+ */
+public final void drawImage(Image srcImage, Rectangle src, Rectangle dest) {
+    drawImage(srcImage, src.x, src.y, src.width, src.height,
+                        dest.x, dest.y, dest.width, dest.height);
+}
+
+/**
+ * Draws a line between the points <code>(x1,y1)</code> and <code>(x2,y2)</code> using the
+ * foreground color.
+ * @param x1 the x coordinate for the first point
+ * @param y1 the y coordinate for the first point
+ * @param x2 the x coordinate for the second point
+ * @param y2 the y coordinate for the second point
+ */
+public abstract void drawLine(int x1, int y1, int x2, int y2);
+
+/**
+ * @see #drawLine(int, int, int, int)
+ */
+public final void drawLine(Point p1, Point p2) {
+    drawLine(p1.x, p1.y, p2.x, p2.y);
+}
+
+/**
+ * Draws the outline of an ellipse that fits inside the rectangle with the given
+ * properties using the foreground color.
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param w the width
+ * @param h the height
+ */
+public abstract void drawOval(int x, int y, int w, int h);
+
+/**
+ * Draws an oval inside the given rectangle using the current foreground color.
+ * @param r the rectangle circumscribing the oval to be drawn
+ * @see #drawOval(int, int, int, int)
+ */
+public final void drawOval(Rectangle r) {
+    drawOval(r.x, r.y, r.width, r.height);
+}
+
+/**
+ * Draws the given path.
+ * @param path the path to draw
+ * @since 3.1
+ */
+public void drawPath(Path path) {
+    subclassFunctionMission();
+}
+
+/**
+ * Draws a pixel, using the foreground color, at the specified point (<code>x</code>,
+ * <code>y</code>).
+ * <p>
+ * Note that the current line attributes do not affect this
+ * operation.
+ * </p>
+ *
+ * @param x the point's x coordinate
+ * @param y the point's y coordinate
+ *
+ */
+public void drawPoint(int x, int y) {
+    drawLine(x, y, x, y);
+}
+
+/**
+ * Draws a closed polygon defined by the given Integer array containing the vertices in
+ * x,y order.  The first and last points in the list will be connected.
+ * @param points the vertices
+ */
+public void drawPolygon(int[] points) {
+    drawPolygon(getPointList(points));
+}
+
+/**
+ * Draws a closed polygon defined by the given <code>PointList</code> containing the
+ * vertices.  The first and last points in the list will be connected.
+ * @param points the vertices
+ */
+public abstract void drawPolygon(PointList points);
+
+/**
+ * Draws a polyline defined by the given Integer array containing the vertices in x,y
+ * order. The first and last points in the list will <b>not</b> be connected.
+ * @param points the vertices
+ */
+public void drawPolyline(int[] points) {
+    drawPolyline(getPointList(points));
+}
+
+/**
+ * Draws a polyline defined by the given <code>PointList</code> containing the vertices.
+ * The first and last points in the list will <b>not</b> be connected.
+ * @param points the vertices
+ */
+public abstract void drawPolyline(PointList points);
+
+/**
+ * Draws a rectangle whose top-left corner is located at the point (x,y) with the given
+ * width and height.
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param width the width
+ * @param height the height
+ */
+public abstract void drawRectangle(int x, int y, int width, int height);
+
+/**
+ * Draws the given rectangle using the current foreground color.
+ * @param r the rectangle to draw
+ * @see #drawRectangle(int, int, int, int)
+ */
+public final void drawRectangle(Rectangle r) {
+    drawRectangle(r.x, r.y, r.width, r.height);
+}
+
+/**
+ * Draws a rectangle with rounded corners using the foreground color.  <i>arcWidth</i> and
+ * <i>arcHeight</i> represent the horizontal and vertical diameter of the corners.
+ *
+ * @param r the rectangle
+ * @param arcWidth the arc width
+ * @param arcHeight the arc height
+ */
+public abstract void drawRoundRectangle(Rectangle r, int arcWidth, int arcHeight);
+
+/**
+ * Draws the given string using the current font and foreground color. No tab expansion or
+ * carriage return processing will be performed. The background of the string will be
+ * transparent.
+ *
+ * @param s the string
+ * @param x the x coordinate
+ * @param y the y coordinate
+ */
+public abstract void drawString(String s, int x, int y);
+
+/**
+ * @see #drawString(String, int, int)
+ */
+public final void drawString(String s, Point p) {
+    drawString(s, p.x, p.y);
+}
+
+
+/**
+ * Draws the given string using the current font and foreground color. Tab expansion and
+ * carriage return processing are performed. The background of the text will be
+ * transparent.
+ *
+ * @param s the text
+ * @param x the x coordinate
+ * @param y the y coordinate
+ */
+public abstract void drawText(String s, int x, int y);
+
+/**
+ * Draws a string using the specified styles. The styles are defined by {@link
+ * GC#drawText(String, int, int, int)}.
+ * @param s the String to draw
+ * @param x the x location
+ * @param y the y location
+ * @param style the styles used to render the string
+ * @since 3.0
+ */
+public void drawText(String s, int x, int y, int style) {
+    subclassFunctionMission();
+}
+
+/**
+ * @see #drawText(String, int, int)
+ */
+public final void drawText(String s, Point p) {
+    drawText(s, p.x, p.y);
+}
+
+/**
+ * Draws a string using the specified styles. The styles are defined by {@link
+ * GC#drawText(String, int, int, int)}.
+ * @param s the String to draw
+ * @param p the point at which to draw the string
+ * @param style the styles used to render the string
+ * @since 3.0
+ */
+public final void drawText(String s, Point p, int style) {
+    drawText(s, p.x, p.y, style);
+}
+
+/**
+ * Renders the specified TextLayout to this Graphics.
+ * @since 3.0
+ * @param layout the TextLayout
+ * @param x the x coordinate
+ * @param y the y coordinate
+ */
+public final void drawTextLayout(TextLayout layout, int x, int y) {
+    drawTextLayout(layout, x, y, -1, -1, null, null);
+}
+
+/**
+ * @param x the x location
+ * @param y the y location
+ * @param layout the TextLayout being rendered
+ * @param selectionStart the start of selection
+ * @param selectionEnd the end of selection
+ * @param selectionForeground the foreground selection color
+ * @param selectionBackground the background selection color
+ * @see Graphics#drawTextLayout(TextLayout, int, int)
+ */
+public void drawTextLayout(TextLayout layout, int x, int y, int selectionStart,
+        int selectionEnd, Color selectionForeground, Color selectionBackground) {
+    subclassFunctionMission();
+}
+
+/**
+ * Fills the interior of an arc located at (<i>x</i>,<i>y</i>) with width <i>w</i> and
+ * height <i>h</i>. The starting angle of the arc (specified in degrees) is <i>offset</i>
+ * and <i>length</i> is the arc's angle (specified in degrees).
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param w the width
+ * @param h the height
+ * @param offset the start angle
+ * @param length the length of the arc
+ */
+public abstract void fillArc(int x, int y, int w, int h, int offset, int length);
+
+/**
+ * @see #fillArc(int, int, int, int, int, int)
+ */
+public final void fillArc(Rectangle r, int offset, int length) {
+    fillArc(r.x, r.y, r.width, r.height, offset, length);
+}
+
+/**
+ * Fills the the given rectangle with a gradient from the foreground color to the
+ * background color. If <i>vertical</i> is <code>true</code>, the gradient will go from
+ * top to bottom.  Otherwise, it will go from left to right.
+ * background color.
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param w the width
+ * @param h the height
+ * @param vertical whether the gradient should be vertical
+ */
+public abstract void fillGradient(int x, int y, int w, int h, bool vertical);
+
+/**
+ * @see #fillGradient(int, int, int, int, bool)
+ */
+public final void fillGradient(Rectangle r, bool vertical) {
+    fillGradient(r.x, r.y, r.width, r.height, vertical);
+}
+
+/**
+ * Fills an ellipse that fits inside the rectangle with the given properties using the
+ * background color.
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param w the width
+ * @param h the height
+ */
+public abstract void fillOval(int x, int y, int w, int h);
+
+/**
+ * @see #fillOval(int, int, int, int)
+ */
+public final void fillOval(Rectangle r) {
+    fillOval(r.x, r.y, r.width, r.height);
+}
+
+/**
+ * Fills the given path.
+ * @param path the path to fill
+ * @since 3.1
+ */
+public void fillPath(Path path) {
+    subclassFunctionMission();
+}
+
+/**
+ * Fills a closed polygon defined by the given Integer array containing the
+ * vertices in x,y order.  The first and last points in the list will be connected.
+ * @param points the vertices
+ */
+public void fillPolygon(int[] points) {
+    fillPolygon(getPointList(points));
+}
+
+/**
+ * Fills a closed polygon defined by the given <code>PointList</code> containing the
+ * vertices.  The first and last points in the list will be connected.
+ * @param points the vertices
+ */
+public abstract void fillPolygon(PointList points);
+
+/**
+ * Fills a rectangle whose top-left corner is located at the point (x,y) with the given
+ * width and height.
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param width the width
+ * @param height the height
+ */
+public abstract void fillRectangle(int x, int y, int width, int height);
+
+/**
+ * Fills the given rectangle using the current background color.
+ * @param r the rectangle to fill
+ * @see #fillRectangle(int, int, int, int)
+ */
+public final void fillRectangle(Rectangle r) {
+    fillRectangle(r.x, r.y, r.width, r.height);
+}
+
+/**
+ * Fills a rectangle with rounded corners using the background color.  <i>arcWidth</i> and
+ * <i>arcHeight</i> represent the horizontal and vertical diameter of the corners.
+ *
+ * @param r the rectangle
+ * @param arcWidth the arc width
+ * @param arcHeight the arc height
+ */
+public abstract void fillRoundRectangle(Rectangle r, int arcWidth, int arcHeight);
+
+/**
+ * Draws the given string using the current font and foreground color. No tab expansion or
+ * carriage return processing will be performed. The background of the string will be
+ * filled with the current background color.
+ *
+ * @param s the string
+ * @param x the x coordinate
+ * @param y the y coordinate
+ */
+public abstract void fillString(String s, int x, int y);
+
+/**
+ * @see #fillString(String, int, int)
+ */
+public final void fillString(String s, Point p) {
+    fillString(s, p.x, p.y);
+}
+
+/**
+ * Draws the given string using the current font and foreground color. Tab expansion and
+ * carriage return processing are performed. The background of the text will be filled
+ * with the current background color.
+ *
+ * @param s the text
+ * @param x the x coordinate
+ * @param y the y coordinate
+ */
+public abstract void fillText(String s, int x, int y);
+
+/**
+ * @see #fillText(String, int, int)
+ */
+public final void fillText(String s, Point p) {
+    fillText(s, p.x, p.y);
+}
+
+/**
+ * Returns the current absolute scaling which will be applied to the underlying Device
+ * when painting to this Graphics.  The default value is 1.0.
+ * @since 3.0
+ * @return the effective absolute scaling factor
+ */
+public double getAbsoluteScale() {
+    return 1.0;
+}
+
+/**
+ * Returns the current alpha value of the graphics.
+ * @return the alpha value
+ * @since 3.1
+ */
+public int getAlpha() {
+    subclassFunctionMission();
+    return 255;
+}
+
+/**
+ * Returns the anti-aliasing setting value, which will be one of <code>DWT.DEFAULT</code>,
+ * <code>DWT.OFF</code> or <code>DWT.ON</code>. Note that this controls anti-aliasing for
+ * all <em>non-text</em> drawing operations.
+ * @see #getTextAntialias()
+ * @return the anti-alias setting
+ * @since 3.1
+ */
+public int getAntialias() {
+    subclassFunctionMission();
+    return DWT.DEFAULT;
+}
+
+/**
+ * Returns the background color used for filling.
+ * @return the background color
+ */
+public abstract Color getBackgroundColor();
+
+/**
+ * Modifies the given rectangle to match the clip region and returns that rectangle.
+ * @param rect the rectangle to hold the clip region
+ * @return the clip rectangle
+ */
+public abstract Rectangle getClip(Rectangle rect);
+
+/**
+ * Returns the fill rule, which will be one of <code>DWT.FILL_EVEN_ODD</code> or
+ * <code>DWT.FILL_WINDING</code>.
+ * @return the fill rule
+ * @since 3.1
+ */
+public int getFillRule() {
+    subclassFunctionMission();
+    return 0;
+}
+
+/**
+ * Returns the font used to draw and fill text.
+ * @return the font
+ */
+public abstract Font getFont();
+
+/**
+ * Returns the font metrics for the current font.
+ * @return the font metrics
+ */
+public abstract FontMetrics getFontMetrics();
+
+/**
+ * Returns the foreground color used to draw lines and text.
+ * @return the foreground color
+ */
+public abstract Color getForegroundColor();
+
+/**
+ * Returns the interpolation setting, which will be one of <code>DWT.DEFAULT</code>,
+ * <code>DWT.NONE</code>, <code>DWT.LOW</code> or <code>DWT.HIGH</code>.
+ * @return the interpolation setting
+ * @since 3.1
+ */
+public int getInterpolation() {
+    subclassFunctionMission();
+    return 0;
+}
+
+/**
+ * Returns the current line cap style, which will be one of the constants
+ * <code>DWT.CAP_FLAT</code>, <code>DWT.CAP_ROUND</code>, or <code>DWT.CAP_SQUARE</code>.
+ *
+ * @return the cap style used for drawing lines
+ * @since 3.1
+ */
+public int getLineCap() {
+    subclassFunctionMission();
+    return DWT.CAP_FLAT;
+}
+
+
+/**
+ * Returns the line join style, which will be one of the constants
+ * <code>DWT.JOIN_MITER</code>, <code>DWT.JOIN_ROUND</code>, or
+ * <code>DWT.JOIN_BEVEL</code>.
+ *
+ * @since 3.1
+ * @return the join style used for drawing lines
+ */
+public int getLineJoin() {
+    subclassFunctionMission();
+    return DWT.JOIN_MITER;
+}
+
+/**
+ * Returns the line style.
+ * @return the line style
+ */
+public abstract int getLineStyle();
+/**
+ * Returns the current line width.
+ * @return the line width
+ */
+public abstract int getLineWidth();
+
+/**
+ * Returns a pointlist containing all the points from the integer array.
+ * @param points an integer array of x,y points
+ * @return the corresponding pointlist
+ */
+private PointList getPointList(int[] points) {
+    PointList pointList = new PointList(points.length / 2);
+    for (int i = 0; (i + 1) < points.length; i += 2)
+        pointList.addPoint(points[i], points[i + 1]);
+    return pointList;
+}
+
+/**
+ * Returns the textual anti-aliasing setting value, which will be one of
+ * <code>DWT.DEFAULT</code>, <code>DWT.OFF</code> or <code>DWT.ON</code>. Note that this
+ * controls anti-aliasing <em>only</em> for text drawing operations.
+ *
+ * @see #getAntialias()
+ * @return the anti-aliasing setting
+ * @since 3.1
+ */
+public int getTextAntialias() {
+    subclassFunctionMission();
+    return DWT.DEFAULT;
+}
+
+/**
+ * Returns <code>true</code> if this graphics object should use XOR mode with painting.
+ * @return whether XOR mode is turned on
+ */
+public abstract bool getXORMode();
+
+/**
+ * Pops the previous state of this graphics object off the stack (if {@link #pushState()}
+ * has previously been called) and restores the current state to that popped state.
+ */
+public abstract void popState();
+
+/**
+ * Pushes the current state of this graphics object onto a stack.
+ */
+public abstract void pushState();
+
+/**
+ * Restores the previous state of this graphics object.
+ */
+public abstract void restoreState();
+
+/**
+ * Rotates the coordinates by the given counter-clockwise angle. All subsequent painting
+ * will be performed in the resulting coordinates. Some functions are illegal when a
+ * rotated coordinates system is in use.  To restore access to those functions, it is
+ * necessary to call restore or pop to return to a non rotated state.
+ * @param degrees the degrees to rotate
+ * @since 3.1
+ */
+public void rotate(float degrees) {
+    subclassFunctionMission();
+}
+
+/**
+ * Scales this graphics object by the given amount.
+ * @param amount the scale factor
+ */
+public abstract void scale(double amount);
+
+/**
+ * Scales the graphics by the given horizontal and vertical components.
+ * @param horizontal the horizontal scaling factor
+ * @param vertical the vertical scaling factor
+ * @since 3.1
+ */
+public void scale(float horizontal, float vertical) {
+    subclassFunctionMission();
+}
+
+/**
+ * Sets the alpha to the given value.  Values may range from 0 to 255.  A value
+ * of 0 is completely transparent.
+ *
+ * @param alpha an alpha value (0-255)
+ * @since 3.1
+ */
+public void setAlpha(int alpha) {
+    subclassFunctionMission();
+}
+
+/**
+ * Sets the anti-aliasing value to the parameter, which must be one of
+ * <code>DWT.DEFAULT</code>, <code>DWT.OFF</code> or <code>DWT.ON</code>. Note that this
+ * controls anti-aliasing for all <em>non-text drawing</em> operations.
+ *
+ * @param value the anti-alias value
+ */
+public void setAntialias(int value) {
+    subclassFunctionMission();
+}
+
+/**
+ * Sets the background color.
+ * @param rgb the new background color
+ */
+public abstract void setBackgroundColor(Color rgb);
+
+/**
+ * Sets the pattern used for fill-type graphics operations. The pattern must not be
+ * disposed while it is being used by the graphics.
+ * @param pattern the background pattern
+ * @since 3.1
+ */
+public void setBackgroundPattern(Pattern pattern) {
+    subclassFunctionMission();
+}
+
+/**
+ * Sets the area which can be affected by drawing operations to the specified
+ * <code>Path</code>.
+
+ * @param path the clipping path
+ * @since 3.1
+ */
+public void setClip(Path path) {
+    subclassFunctionMission();
+}
+
+/**
+ * Sets the clip rectangle. Painting will <b>not</b> occur outside this area.
+ * @param r the new clip rectangle
+ */
+public abstract void setClip(Rectangle r);
+
+/**
+ * Sets the fill rule to the given value, which must be one of
+ * <code>DWT.FILL_EVEN_ODD</code> or <code>DWT.FILL_WINDING</code>.
+ * @param rule the fill rule
+ * @since 3.1
+ */
+public void setFillRule(int rule) {
+    subclassFunctionMission();
+}
+
+/**
+ * Sets the font.
+ * @param f the new font
+ */
+public abstract void setFont(Font f);
+
+/**
+ * Sets the foreground color.
+ * @param rgb the new foreground color
+ */
+public abstract void setForegroundColor(Color rgb);
+
+/**
+ * Sets the foreground pattern for draw and text operations. The pattern must not be
+ * disposed while it is being referenced by the graphics.
+ * @param pattern the foreground pattern
+ * @since 3.1
+ */
+public void setForegroundPattern(Pattern pattern) {
+    subclassFunctionMission();
+}
+
+/**
+ * Sets the interpolation setting to the given value, which must be one of
+ * <code>DWT.DEFAULT</code>, <code>DWT.NONE</code>,  <code>DWT.LOW</code> or
+ * <code>DWT.HIGH</code>. This setting is relevant when working with Images.
+ * @param interpolation the interpolation
+ * @since 3.1
+ */
+public void setInterpolation(int interpolation) {
+    subclassFunctionMission();
+}
+
+/**
+ * Sets the line cap style to the argument, which must be one of the constants
+ * <code>DWT.CAP_FLAT</code>, <code>DWT.CAP_ROUND</code>, or <code>DWT.CAP_SQUARE</code>.
+ * @param cap the line cap
+ * @since 3.1
+ */
+public void setLineCap(int cap) {
+    subclassFunctionMission();
+}
+
+/**
+ * Sets the dash pattern when the custom line style is in use. Because this
+ * feature is rarely used, the dash pattern may not be preserved when calling
+ * {@link #pushState()} and {@link #popState()}.
+ * @param dash the pixel pattern
+ * @since 3.1
+ */
+public void setLineDash(int dash[]) {
+    subclassFunctionMission();
+}
+
+/**
+ * Sets the line join style to the argument, which must be one of the constants
+ * <code>DWT.JOIN_MITER</code>, <code>DWT.JOIN_ROUND</code>, or
+ * <code>DWT.JOIN_BEVEL</code>.
+ * @param join the join type
+ * @since 3.1
+ */
+public void setLineJoin(int join) {
+    subclassFunctionMission();
+}
+
+/**
+ * Sets the line style to the argument, which must be one of the constants
+ * <code>DWT.LINE_SOLID</code>, <code>DWT.LINE_DASH</code>, <code>DWT.LINE_DOT</code>,
+ * <code>DWT.LINE_DASHDOT</code> or <code>DWT.LINE_DASHDOTDOT</code>.
+ * @param style the new style
+ */
+public abstract void setLineStyle(int style);
+
+/**
+ * Sets the line width.
+ *
+ * @param width the new width
+ */
+public abstract void setLineWidth(int width);
+
+/**
+ * Sets the textual anti-aliasing value to the parameter, which must be one of
+ * <code>DWT.DEFAULT</code>, <code>DWT.OFF</code> or <code>DWT.ON</code>. Note that this
+ * controls anti-aliasing only for all <em>text drawing</em> operations.
+ *
+ * @param value the textual anti-alias setting
+ * @since 3.1
+ */
+public void setTextAntialias(int value) {
+    subclassFunctionMission();
+}
+
+/**
+ * Modifies the current transformation by shearing the graphics in the specified
+ * horizontal and vertical amounts. Shearing can be used to produce effects like Italic
+ * fonts.
+ * @param horz the horizontal shearing amount
+ * @param vert the vertical shearming amount
+ * @since 3.1
+ */
+public void shear(float horz, float vert) {
+    subclassFunctionMission();
+}
+
+/**
+ * Sets the XOR mode.
+ * @param b the new XOR mode
+ */
+public abstract void setXORMode(bool b);
+
+private void subclassFunctionMission() {
+    throw new RuntimeException( "The class: " ~ this.classinfo.name //$NON-NLS-1$
+            ~ " has not implemented this new graphics function"); //$NON-NLS-1$
+}
+
+/**
+ * Translates the receiver's coordinates by the specified x and y amounts. All
+ * subsequent painting will be performed in the resulting coordinate system. Integer
+ * translation used by itself does not require or start the use of the advanced
+ * graphics system in DWT. It is emulated until advanced graphics are triggered.
+ * @param dx the horizontal offset
+ * @param dy the vertical offset
+ */
+public abstract void translate(int dx, int dy);
+
+
+/**
+ * Modifies the current transform by translating the given x and y amounts. All
+ * subsequent painting will be performed in the resulting coordinate system.
+ * @param dx the horizontal offset
+ * @param dy the vertical offset
+ */
+public void translate(float dx, float dy) {
+    subclassFunctionMission();
+}
+
+/**
+ * @see #translate(int, int)
+ */
+public final void translate(Point pt) {
+    translate(pt.x, pt.y);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/GraphicsSource.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.GraphicsSource;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.Graphics;
+
+/**
+ * Provides a {@link dwtx.draw2d.Graphics} object for painting.
+ */
+public interface GraphicsSource {
+
+/**
+ * Returns a Graphics for the rectangular region requested. May return <code>null</code>.
+ * @param region The rectangular region
+ * @return A new Graphics object for the given region
+ */
+Graphics getGraphics(Rectangle region);
+
+/**
+ * Tells the GraphicsSource that you have finished using that region.
+ * @param region The rectangular region that that no longer needs the Graphics
+ */
+void flushGraphics(Rectangle region);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/GridData.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,511 @@
+/*******************************************************************************
+ * 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
+ *     Asim Ullah - Ported for use in draw2d (c.f Bugzilla 71684).[Sep 10, 2004]
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.GridData;
+
+import dwt.dwthelper.utils;
+
+
+import dwt.DWT;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.IFigure;
+
+/**
+ * <code>GridData</code> is the layout data object associated with
+ * <code>GridLayout</code>. To set a <code>GridData</code> object into a
+ * <code>Figure</code>, you use the <code>setConstraint()</code> method of
+ * <code>GridLayout</code> to map the <code>Figure</code> to its layout
+ * <code>GridData</code>.
+ * <p>
+ * There are two ways to create a <code>GridData</code> object with certain
+ * fields set. The first is to set the fields directly, like this:
+ *
+ * <pre>
+ * GridData gridData = new GridData();
+ * gridData.horizontalAlignment = GridData.FILL;
+ * gridData.grabExcessHorizontalSpace = true;
+ *
+ * // associate the figure to the GridData object
+ * myGridlayout.setConstraint(myFigure, gridData);
+ * </pre>
+ *
+ * The second is to take advantage of convenience style bits defined by
+ * <code>GridData</code>:
+ *
+ * <pre>
+ * GridData gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL
+ *      | GridData.GRAB_HORIZONTAL);
+ * </pre>
+ *
+ * </p>
+ * <p>
+ * NOTE: Do not reuse <code>GridData</code> objects. Every child in the parent
+ * <code>Figure</code> that is managed by the <code>GridLayout</code> must
+ * have a unique <code>GridData</code> object. If the layout data for a Grid
+ * member in a <code>GridLayout</code> is null at layout time, a unique
+ * <code>GridData</code> object is created for it.
+ * </p>
+ *
+ * @see GridLayout
+ */
+public final class GridData {
+    /**
+     * verticalAlignment specifies how figures will be positioned vertically
+     * within a cell.
+     *
+     * The default value is CENTER.
+     *
+     * Possible values are:
+     *
+     * DWT.BEGINNING (or DWT.TOP): Position the figure at the top of the cell
+     * DWT.CENTER: Position the figure in the vertical center of the cell
+     * DWT.END (or DWT.BOTTOM): Position the figure at the bottom of the cell
+     * DWT.FILL: Resize the figure to fill the cell vertically
+     */
+    public int verticalAlignment = CENTER;
+
+    /**
+     * horizontalAlignment specifies how figures will be positioned horizontally
+     * within a cell.
+     *
+     * The default value is BEGINNING.
+     *
+     * Possible values are:
+     *
+     * DWT.BEGINNING (or DWT.LEFT): Position the figure at the left of the cell
+     * DWT.CENTER: Position the figure in the horizontal center of the cell
+     * DWT.END (or DWT.RIGHT): Position the figure at the right of the cell
+     * DWT.FILL: Resize the figure to fill the cell horizontally
+     */
+    public int horizontalAlignment = BEGINNING;
+
+    /**
+     * widthHint specifies a minimum width for the column. A value of
+     * DWT.DEFAULT indicates that no minimum width is specified.
+     *
+     * The default value is DWT.DEFAULT.
+     */
+    public int widthHint = DWT.DEFAULT;
+
+    /**
+     * heightHint specifies a minimum height for the row. A value of DWT.DEFAULT
+     * indicates that no minimum height is specified.
+     *
+     * The default value is DWT.DEFAULT.
+     */
+    public int heightHint = DWT.DEFAULT;
+
+    /**
+     * horizontalIndent specifies the number of pixels of indentation that will
+     * be placed along the left side of the cell.
+     *
+     * The default value is 0.
+     */
+    public int horizontalIndent = 0;
+
+    /**
+     * horizontalSpan specifies the number of column cells that the figure will
+     * take up.
+     *
+     * The default value is 1.
+     */
+    public int horizontalSpan = 1;
+
+    /**
+     * verticalSpan specifies the number of row cells that the figure will take
+     * up.
+     *
+     * The default value is 1.
+     */
+    public int verticalSpan = 1;
+
+    /**
+     * grabExcessHorizontalSpace specifies whether the cell will be made wide
+     * enough to fit the remaining horizontal space.
+     *
+     * The default value is false.
+     */
+    public bool grabExcessHorizontalSpace = false;
+
+    /**
+     * grabExcessVerticalSpace specifies whether the cell will be made tall
+     * enough to fit the remaining vertical space.
+     *
+     * The default value is false.
+     */
+    public bool grabExcessVerticalSpace = false;
+
+    /**
+     * Value for horizontalAlignment or verticalAlignment. Position the figure
+     * at the top or left of the cell. Not recommended. Use DWT.BEGINNING,
+     * DWT.TOP or DWT.LEFT instead.
+     */
+    public static const int BEGINNING = DWT.BEGINNING;
+
+    /**
+     * Value for horizontalAlignment or verticalAlignment. Position the figure
+     * in the vertical or horizontal center of the cell Not recommended. Use
+     * DWT.CENTER instead.
+     */
+    public static const int CENTER = 2;
+
+    /**
+     * Value for horizontalAlignment or verticalAlignment. Position the figure
+     * at the bottom or right of the cell Not recommended. Use DWT.END,
+     * DWT.BOTTOM or DWT.RIGHT instead.
+     */
+    public static const int END = 3;
+
+    /**
+     * Value for horizontalAlignment or verticalAlignment. Resize the figure to
+     * fill the cell horizontally or vertically. Not recommended. Use DWT.FILL
+     * instead.
+     */
+    public static const int FILL = DWT.FILL;
+
+    /**
+     * Style bit for <code>new GridData(int)</code>. Position the figure at
+     * the top of the cell. Not recommended. Use
+     * <code>new GridData(int, DWT.BEGINNING, bool, bool)</code>
+     * instead.
+     */
+    public static const int VERTICAL_ALIGN_BEGINNING = 1 << 1;
+
+    /**
+     * Style bit for <code>new GridData(int)</code> to position the figure in
+     * the vertical center of the cell. Not recommended. Use
+     * <code>new GridData(int, DWT.CENTER, bool, bool)</code> instead.
+     */
+    public static const int VERTICAL_ALIGN_CENTER = 1 << 2;
+
+    /**
+     * Style bit for <code>new GridData(int)</code> to position the figure at
+     * the bottom of the cell. Not recommended. Use
+     * <code>new GridData(int, DWT.END, bool, bool)</code> instead.
+     */
+    public static const int VERTICAL_ALIGN_END = 1 << 3;
+
+    /**
+     * Style bit for <code>new GridData(int)</code> to resize the figure to
+     * fill the cell vertically. Not recommended. Use
+     * <code>new GridData(int, DWT.FILL, bool, bool)</code> instead
+     */
+    public static const int VERTICAL_ALIGN_FILL = 1 << 4;
+
+    /**
+     * Style bit for <code>new GridData(int)</code> to position the figure at
+     * the left of the cell. Not recommended. Use
+     * <code>new GridData(DWT.BEGINNING, int, bool, bool)</code>
+     * instead.
+     */
+    public static const int HORIZONTAL_ALIGN_BEGINNING = 1 << 5;
+
+    /**
+     * Style bit for <code>new GridData(int)</code> to position the figure in
+     * the horizontal center of the cell. Not recommended. Use
+     * <code>new GridData(DWT.CENTER, int, bool, bool)</code> instead.
+     */
+    public static const int HORIZONTAL_ALIGN_CENTER = 1 << 6;
+
+    /**
+     * Style bit for <code>new GridData(int)</code> to position the figure at
+     * the right of the cell. Not recommended. Use
+     * <code>new GridData(DWT.END, int, bool, bool)</code> instead.
+     */
+    public static const int HORIZONTAL_ALIGN_END = 1 << 7;
+
+    /**
+     * Style bit for <code>new GridData(int)</code> to resize the figure to
+     * fill the cell horizontally. Not recommended. Use
+     * <code>new GridData(DWT.FILL, int, bool, bool)</code> instead.
+     */
+    public static const int HORIZONTAL_ALIGN_FILL = 1 << 8;
+
+    /**
+     * Style bit for <code>new GridData(int)</code> to resize the figure to
+     * fit the remaining horizontal space. Not recommended. Use
+     * <code>new GridData(int, int, true, bool)</code> instead.
+     */
+    public static const int GRAB_HORIZONTAL = 1 << 9;
+
+    /**
+     * Style bit for <code>new GridData(int)</code> to resize the figure to
+     * fit the remaining vertical space. Not recommended. Use
+     * <code>new GridData(int, int, bool, true)</code> instead.
+     */
+    public static const int GRAB_VERTICAL = 1 << 10;
+
+    /**
+     * Style bit for <code>new GridData(int)</code> to resize the figure to
+     * fill the cell vertically and to fit the remaining vertical space.
+     * FILL_VERTICAL = VERTICAL_ALIGN_FILL | GRAB_VERTICAL Not recommended. Use
+     * <code>new GridData(int, DWT.FILL, bool, true)</code> instead.
+     */
+    public static const int FILL_VERTICAL = VERTICAL_ALIGN_FILL | GRAB_VERTICAL;
+
+    /**
+     * Style bit for <code>new GridData(int)</code> to resize the figure to
+     * fill the cell horizontally and to fit the remaining horizontal space.
+     * FILL_HORIZONTAL = HORIZONTAL_ALIGN_FILL | GRAB_HORIZONTAL Not
+     * recommended. Use <code>new GridData(DWT.FILL, int, true, bool)</code>
+     * instead.
+     */
+    public static const int FILL_HORIZONTAL = HORIZONTAL_ALIGN_FILL
+            | GRAB_HORIZONTAL;
+
+    /**
+     * Style bit for <code>new GridData(int)</code> to resize the figure to
+     * fill the cell horizontally and vertically and to fit the remaining
+     * horizontal and vertical space. FILL_BOTH = FILL_VERTICAL |
+     * FILL_HORIZONTAL Not recommended. Use
+     * <code>new GridData(DWT.FILL, DWT.FILL, true, true)</code> instead.
+     */
+    public static const int FILL_BOTH = FILL_VERTICAL | FILL_HORIZONTAL;
+
+    int cacheWidth = -1, cacheHeight = -1;
+    int[][] cache;
+    int cacheIndex = -1;
+
+    /**
+     * Constructs a new instance of GridData using default values.
+     */
+    public this() {
+        cache = new int[][](2,4);
+    }
+
+    /**
+     * Constructs a new instance based on the GridData style. This constructor
+     * is not recommended.
+     *
+     * @param style
+     *            the GridData style
+     */
+    public this(int style) {
+        this();
+        if ((style & VERTICAL_ALIGN_BEGINNING) !is 0)
+            verticalAlignment = BEGINNING;
+        if ((style & VERTICAL_ALIGN_CENTER) !is 0)
+            verticalAlignment = CENTER;
+        if ((style & VERTICAL_ALIGN_FILL) !is 0)
+            verticalAlignment = FILL;
+        if ((style & VERTICAL_ALIGN_END) !is 0)
+            verticalAlignment = END;
+        if ((style & HORIZONTAL_ALIGN_BEGINNING) !is 0)
+            horizontalAlignment = BEGINNING;
+        if ((style & HORIZONTAL_ALIGN_CENTER) !is 0)
+            horizontalAlignment = CENTER;
+        if ((style & HORIZONTAL_ALIGN_FILL) !is 0)
+            horizontalAlignment = FILL;
+        if ((style & HORIZONTAL_ALIGN_END) !is 0)
+            horizontalAlignment = END;
+        grabExcessHorizontalSpace = (style & GRAB_HORIZONTAL) !is 0;
+        grabExcessVerticalSpace = (style & GRAB_VERTICAL) !is 0;
+    }
+
+    /**
+     * Constructs a new instance of GridData according to the parameters.
+     *
+     * @param horizontalAlignment
+     *            how figure will be positioned horizontally within a cell
+     * @param verticalAlignment
+     *            how figure will be positioned vertically within a cell
+     * @param grabExcessHorizontalSpace
+     *            whether cell will be made wide enough to fit the remaining
+     *            horizontal space
+     * @param grabExcessVerticalSpace
+     *            whether cell will be made high enough to fit the remaining
+     *            vertical space
+     *
+     */
+    public this(int horizontalAlignment, int verticalAlignment,
+            bool grabExcessHorizontalSpace, bool grabExcessVerticalSpace) {
+        this(horizontalAlignment, verticalAlignment, grabExcessHorizontalSpace,
+                grabExcessVerticalSpace, 1, 1);
+    }
+
+    /**
+     * Constructs a new instance of GridData according to the parameters.
+     *
+     * @param horizontalAlignment
+     *            how figure will be positioned horizontally within a cell
+     * @param verticalAlignment
+     *            how figure will be positioned vertically within a cell
+     * @param grabExcessHorizontalSpace
+     *            whether cell will be made wide enough to fit the remaining
+     *            horizontal space
+     * @param grabExcessVerticalSpace
+     *            whether cell will be made high enough to fit the remaining
+     *            vertical space
+     * @param horizontalSpan
+     *            the number of column cells that the figure will take up
+     * @param verticalSpan
+     *            the number of row cells that the figure will take up
+     *
+     */
+    public this(int horizontalAlignment, int verticalAlignment,
+            bool grabExcessHorizontalSpace, bool grabExcessVerticalSpace,
+            int horizontalSpan, int verticalSpan) {
+        this();
+        this.horizontalAlignment = horizontalAlignment;
+        this.verticalAlignment = verticalAlignment;
+        this.grabExcessHorizontalSpace = grabExcessHorizontalSpace;
+        this.grabExcessVerticalSpace = grabExcessVerticalSpace;
+        this.horizontalSpan = horizontalSpan;
+        this.verticalSpan = verticalSpan;
+    }
+
+    /**
+     * Constructs a new instance of GridData according to the parameters. A
+     * value of DWT.DEFAULT indicates that no minimum width or no minumum height
+     * is specified.
+     *
+     * @param width
+     *            a minimum width for the column
+     * @param height
+     *            a minimum height for the row
+     *
+     */
+    public this(int width, int height) {
+        this();
+        this.widthHint = width;
+        this.heightHint = height;
+    }
+
+    Dimension computeSize(IFigure figure, bool flushCache) {
+        if (cacheWidth !is -1 && cacheHeight !is -1) {
+            return new Dimension(cacheWidth, cacheHeight);
+        }
+        for (int i = 0; i < cacheIndex + 1; i++) {
+            if (cache[i][0] is widthHint && cache[i][1] is heightHint) {
+                cacheWidth = cache[i][2];
+                cacheHeight = cache[i][3];
+                return new Dimension(cacheWidth, cacheHeight);
+            }
+        }
+
+        Dimension size = figure.getPreferredSize(widthHint, heightHint);
+        if (widthHint !is -1)
+            size.width = widthHint;
+        if (heightHint !is -1)
+            size.height = heightHint;
+
+        if (cacheIndex < cache.length - 1)
+            cacheIndex++;
+        cache[cacheIndex][0] = widthHint;
+        cache[cacheIndex][1] = heightHint;
+        cacheWidth = cache[cacheIndex][2] = size.width;
+        cacheHeight = cache[cacheIndex][3] = size.height;
+        return size;
+    }
+
+    void flushCache() {
+        cacheWidth = cacheHeight = -1;
+        cacheIndex = -1;
+    }
+
+    String getName() {
+        String string = this.classinfo.name;
+        int index = string.lastIndexOf('.');
+        if (index is -1)
+            return string;
+        return string.substring(index + 1, string.length);
+    }
+
+    public String toString() {
+
+        String hAlign = ""; //$NON-NLS-1$
+        switch (horizontalAlignment) {
+        case DWT.FILL:
+            hAlign = "DWT.FILL"; //$NON-NLS-1$
+            break;
+        case DWT.BEGINNING:
+            hAlign = "DWT.BEGINNING"; //$NON-NLS-1$
+            break;
+        case DWT.LEFT:
+            hAlign = "DWT.LEFT"; //$NON-NLS-1$
+            break;
+        case DWT.END:
+            hAlign = "DWT.END"; //$NON-NLS-1$
+            break;
+        case END:
+            hAlign = "GridData.END"; //$NON-NLS-1$
+            break;
+        case DWT.RIGHT:
+            hAlign = "DWT.RIGHT"; //$NON-NLS-1$
+            break;
+        case DWT.CENTER:
+            hAlign = "DWT.CENTER"; //$NON-NLS-1$
+            break;
+        case CENTER:
+            hAlign = "GridData.CENTER"; //$NON-NLS-1$
+            break;
+        default:
+            hAlign = "Undefined " ~ Integer.toString(horizontalAlignment); //$NON-NLS-1$
+            break;
+        }
+        String vAlign = ""; //$NON-NLS-1$
+        switch (verticalAlignment) {
+        case DWT.FILL:
+            vAlign = "DWT.FILL"; //$NON-NLS-1$
+            break;
+        case DWT.BEGINNING:
+            vAlign = "DWT.BEGINNING"; //$NON-NLS-1$
+            break;
+        case DWT.TOP:
+            vAlign = "DWT.TOP"; //$NON-NLS-1$
+            break;
+        case DWT.END:
+            vAlign = "DWT.END"; //$NON-NLS-1$
+            break;
+        case END:
+            vAlign = "GridData.END"; //$NON-NLS-1$
+            break;
+        case DWT.BOTTOM:
+            vAlign = "DWT.BOTTOM"; //$NON-NLS-1$
+            break;
+        case DWT.CENTER:
+            vAlign = "DWT.CENTER"; //$NON-NLS-1$
+            break;
+        case CENTER:
+            vAlign = "GridData.CENTER"; //$NON-NLS-1$
+            break;
+        default:
+            vAlign = "Undefined " ~ Integer.toString(verticalAlignment); //$NON-NLS-1$
+            break;
+        }
+        String string = getName() ~ " {"; //$NON-NLS-1$
+        string ~= "horizontalAlignment=" ~ hAlign ~ " "; //$NON-NLS-1$ //$NON-NLS-2$
+        if (horizontalIndent !is 0)
+            string ~= "horizontalIndent=" ~ Integer.toString(horizontalIndent) ~ " "; //$NON-NLS-1$ //$NON-NLS-2$
+        if (horizontalSpan !is 1)
+            string ~= "horizontalSpan=" ~ Integer.toString(horizontalSpan) ~ " "; //$NON-NLS-1$//$NON-NLS-2$
+        if (grabExcessHorizontalSpace)
+            string ~= "grabExcessHorizontalSpace=" ~ Integer.toString(grabExcessHorizontalSpace) //$NON-NLS-1$
+                    ~ " "; //$NON-NLS-1$
+        if (widthHint !is DWT.DEFAULT)
+            string ~= "widthHint=" ~ Integer.toString(widthHint) ~ " "; //$NON-NLS-1$ //$NON-NLS-2$
+        string ~= "verticalAlignment=" ~ vAlign ~ " "; //$NON-NLS-1$ //$NON-NLS-2$
+        if (verticalSpan !is 1)
+            string ~= "verticalSpan=" ~ Integer.toString(verticalSpan) ~ " "; //$NON-NLS-1$ //$NON-NLS-2$
+        if (grabExcessVerticalSpace)
+            string ~= "grabExcessVerticalSpace=" ~ Integer.toString(grabExcessVerticalSpace) //$NON-NLS-1$
+                    ~ " "; //$NON-NLS-1$
+        if (heightHint !is DWT.DEFAULT)
+            string ~= "heightHint=" ~ Integer.toString(heightHint) ~ " "; //$NON-NLS-1$ //$NON-NLS-2$
+        string = string.trim();
+        string ~= "}"; //$NON-NLS-1$
+        return string;
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/GridLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,799 @@
+/*******************************************************************************
+ * 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
+ *     Asim Ullah - Ported for use in draw2d (c.f Bugzilla 71684). [Sep 10, 2004]
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.GridLayout;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwt.DWT;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.AbstractLayout;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.GridData;
+
+/**
+ * Lays out children into a Grid arrangement in which overall aligment and
+ * spacing can be configured, as well as specfic layout requirements for the
+ * each individual member of the GridLayout. This layout is a Draw2D port of the
+ * swt GridLayout.
+ *
+ * <code>GridLayout</code> has a number of configuration fields, and the
+ * Figures it lays out can have an associated layout data object, called
+ * <code>GridData</code> (similar to the swt GridData object). The power of
+ * <code>GridLayout</code> lies in the ability to configure
+ * <code>GridData</code> for each Figure in the layout.
+ * <p>
+ * The following code creates a container Figure managed by a
+ * <code>GridLayout</code> with 2 columns, containing 3 RectangleFigure
+ * shapes, the last of which has been given further layout instructions. Note
+ * that it is the <code>GridLayout</code> method <code>setConstraint</code>
+ * that binds the child <code>Figure</code> to its layout
+ * <code>GridData</code> object.
+ *
+ * <pre>
+ * Figure container = new Figure();
+ * GridLayout gridLayout = new GridLayout();
+ * gridLayout.numColumns = 2;
+ * container.setLayout(gridLayout);
+ *
+ * Shape rect;
+ * rect = new RectangleFigure();
+ * container.add(rect);
+ *
+ * rect = new RectangleFigure();
+ * container.add(rect);
+ *
+ * rect = new RectangleFigure();
+ * GridData gridData = new GridData();
+ * gridData.widthHint = 150;
+ * layout.setConstraint(rect, gridData);
+ *
+ * container.add(rect);
+ * </pre>
+ *
+ * <p>
+ * The <code>numColumns</code> field is the most important field in a
+ * <code>GridLayout</code>. Widgets are laid out in columns from left to
+ * right, and a new row is created when <code>numColumns</code>+ 1 figures
+ * are added to the <code>Figure<code> parent container.
+ *
+ * @see GridData
+ *
+ */
+public class GridLayout : AbstractLayout {
+
+    /**
+     * numColumns specifies the number of cell columns in the layout.
+     *
+     * The default value is 1.
+     */
+    public int numColumns = 1;
+
+    /**
+     * makeColumnsEqualWidth specifies whether all columns in the layout will be
+     * forced to have the same width.
+     *
+     * The default value is false.
+     */
+    public bool makeColumnsEqualWidth = false;
+
+    /**
+     * 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 5.
+     */
+    public int marginWidth = 5;
+
+    /**
+     * 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 5.
+     */
+    public int marginHeight = 5;
+
+    /**
+     * horizontalSpacing specifies the number of pixels between the right edge
+     * of one cell and the left edge of its neighbouring cell to the right.
+     *
+     * The default value is 5.
+     */
+    public int horizontalSpacing = 5;
+
+    /**
+     * verticalSpacing specifies the number of pixels between the bottom edge of
+     * one cell and the top edge of its neighbouring cell underneath.
+     *
+     * The default value is 5.
+     */
+    public int verticalSpacing = 5;
+
+    /** The layout contraints */
+    protected Map constraints;
+
+    /**
+     * Default Constructor
+     */
+    public this() {
+        //super();
+        constraints = new HashMap();
+    }
+
+    /**
+     * Constructs a new instance of this class given the number of columns, and
+     * whether or not the columns should be forced to have the same width.
+     *
+     * @param numColumns
+     *            the number of columns in the grid
+     * @param makeColumnsEqualWidth
+     *            whether or not the columns will have equal width
+     *
+     */
+    public this(int numColumns, bool makeColumnsEqualWidth) {
+        this.numColumns = numColumns;
+        this.makeColumnsEqualWidth = makeColumnsEqualWidth;
+    }
+
+    /**
+     * @param child
+     * @param wHint
+     * @param hHint
+     * @return the child size.
+     */
+    protected Dimension getChildSize(IFigure child, int wHint, int hHint) {
+        return child.getPreferredSize(wHint, hHint);
+    }
+
+    GridData getData(IFigure[][] grid, int row, int column, int rowCount,
+            int columnCount, bool first) {
+        IFigure figure = grid[row][column];
+        if (figure !is null) {
+            GridData data = cast(GridData) getConstraint(figure);
+            int hSpan = Math.max(1, Math.min(data.horizontalSpan, columnCount));
+            int vSpan = Math.max(1, data.verticalSpan);
+            int i = first ? row + vSpan - 1 : row - vSpan + 1;
+            int j = first ? column + hSpan - 1 : column - hSpan + 1;
+            if (0 <= i && i < rowCount) {
+                if (0 <= j && j < columnCount) {
+                    if (figure is grid[i][j])
+                        return data;
+                }
+            }
+        }
+        return null;
+    }
+
+    void initChildren(IFigure container) {
+        List children = container.getChildren();
+        for (int i = 0; i < children.size(); i++) {
+            IFigure child = cast(IFigure) children.get(i);
+            if (child.getLayoutManager() is null)
+                child.setLayoutManager(this);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.draw2d.AbstractLayout#calculatePreferredSize(dwtx.draw2d.IFigure,
+     *      int, int)
+     */
+    protected Dimension calculatePreferredSize(IFigure container, int wHint,
+            int hHint) {
+        Dimension size = layout(container, false, 0, 0, wHint, hHint, /* flushCache */
+        true);
+        if (wHint !is DWT.DEFAULT)
+            size.width = wHint;
+        if (hHint !is DWT.DEFAULT)
+            size.height = hHint;
+
+        return size;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.draw2d.LayoutManager#layout(dwtx.draw2d.IFigure)
+     */
+    public void layout(IFigure container) {
+        // initChildren( container);
+        Rectangle rect = container.getClientArea();
+        layout(container, true, rect.x, rect.y, rect.width, rect.height, /* flushCache */
+        true);
+
+    }
+
+    Dimension layout(IFigure container, bool move, int x, int y, int width,
+            int height, bool flushCache) {
+        if (numColumns < 1)
+            return new Dimension(marginWidth * 2, marginHeight * 2);
+        List children = container.getChildren();
+        for (int i = 0; i < children.size(); i++) {
+            IFigure child = cast(IFigure) children.get(i);
+
+            GridData data = cast(GridData) getConstraint(child);
+            if (data is null)
+                setConstraint(child, data = new GridData());
+            if (flushCache)
+                data.flushCache();
+            data.computeSize(child, flushCache);
+        }
+
+        /* Build the grid */
+        int row = 0, column = 0, rowCount = 0, columnCount = numColumns;
+        IFigure[][] grid = new IFigure[][](4,columnCount);
+        for (int i = 0; i < children.size(); i++) {
+            IFigure child = cast(IFigure) children.get(i);
+            GridData data = cast(GridData) getConstraint(child);
+            int hSpan = Math.max(1, Math.min(data.horizontalSpan, columnCount));
+            int vSpan = Math.max(1, data.verticalSpan);
+            while (true) {
+                int lastRow = row + vSpan;
+                if (lastRow >= grid.length) {
+                    IFigure[][] newGrid = new IFigure[][](lastRow + 4, columnCount );
+                    SimpleType!(IFigure[]).arraycopy(grid, 0, newGrid, 0, grid.length);
+                    grid = newGrid;
+                }
+                if (grid[row] is null) {
+                    grid[row] = new IFigure[columnCount];
+                }
+                while (column < columnCount && grid[row][column] !is null) {
+                    column++;
+                }
+                int endCount = column + hSpan;
+                if (endCount <= columnCount) {
+                    int index = column;
+                    while (index < endCount && grid[row][index] is null) {
+                        index++;
+                    }
+                    if (index is endCount)
+                        break;
+                    column = index;
+                }
+                if (column + hSpan >= columnCount) {
+                    column = 0;
+                    row++;
+                }
+            }
+            for (int j = 0; j < vSpan; j++) {
+                if (grid[row + j] is null) {
+                    grid[row + j] = new IFigure[columnCount];
+                }
+                for (int k = 0; k < hSpan; k++) {
+                    grid[row + j][column + k] = child;
+                }
+            }
+            rowCount = Math.max(rowCount, row + vSpan);
+            column += hSpan;
+        }
+
+        /* Column widths */
+        int availableWidth = width - horizontalSpacing * (columnCount - 1)
+                - marginWidth * 2;
+        int expandCount = 0;
+        int[] widths = new int[columnCount];
+        int[] minWidths = new int[columnCount];
+        bool[] expandColumn = new bool[columnCount];
+        for (int j = 0; j < columnCount; j++) {
+            for (int i = 0; i < rowCount; i++) {
+                GridData data = getData(grid, i, j, rowCount, columnCount, true);
+                if (data !is null) {
+                    int hSpan = Math.max(1, Math.min(data.horizontalSpan,
+                            columnCount));
+                    if (hSpan is 1) {
+                        int w = data.cacheWidth + data.horizontalIndent;
+                        widths[j] = Math.max(widths[j], w);
+                        if (data.grabExcessHorizontalSpace) {
+                            if (!expandColumn[j])
+                                expandCount++;
+                            expandColumn[j] = true;
+                        }
+                        if (data.widthHint !is DWT.DEFAULT
+                                || !data.grabExcessHorizontalSpace) {
+                            minWidths[j] = Math.max(minWidths[j], w);
+                        }
+                    }
+                }
+            }
+            for (int i = 0; i < rowCount; i++) {
+                GridData data = getData(grid, i, j, rowCount, columnCount,
+                        false);
+                if (data !is null) {
+                    int hSpan = Math.max(1, Math.min(data.horizontalSpan,
+                            columnCount));
+                    if (hSpan > 1) {
+                        int spanWidth = 0, spanMinWidth = 0, spanExpandCount = 0;
+                        for (int k = 0; k < hSpan; k++) {
+                            spanWidth += widths[j - k];
+                            spanMinWidth += minWidths[j - k];
+                            if (expandColumn[j - k])
+                                spanExpandCount++;
+                        }
+                        if (data.grabExcessHorizontalSpace
+                                && spanExpandCount is 0) {
+                            expandCount++;
+                            expandColumn[j] = true;
+                        }
+                        int w = data.cacheWidth + data.horizontalIndent
+                                - spanWidth - (hSpan - 1) * horizontalSpacing;
+                        if (w > 0) {
+                            if (spanExpandCount is 0) {
+                                widths[j] += w;
+                            } else {
+                                int delta = w / spanExpandCount;
+                                int remainder = w % spanExpandCount, last = -1;
+                                for (int k = 0; k < hSpan; k++) {
+                                    if (expandColumn[j - k]) {
+                                        widths[last = j - k] += delta;
+                                    }
+                                }
+                                if (last > -1)
+                                    widths[last] += remainder;
+                            }
+                        }
+                        if (data.widthHint !is DWT.DEFAULT
+                                || !data.grabExcessHorizontalSpace) {
+                            w = data.cacheWidth + data.horizontalIndent
+                                    - spanMinWidth - (hSpan - 1)
+                                    * horizontalSpacing;
+                            if (w > 0) {
+                                if (spanExpandCount is 0) {
+                                    minWidths[j] += w;
+                                } else {
+                                    int delta = w / spanExpandCount;
+                                    int remainder = w % spanExpandCount, last = -1;
+                                    for (int k = 0; k < hSpan; k++) {
+                                        if (expandColumn[j - k]) {
+                                            minWidths[last = j - k] += delta;
+                                        }
+                                    }
+                                    if (last > -1)
+                                        minWidths[last] += remainder;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        if (makeColumnsEqualWidth) {
+            int minColumnWidth = 0;
+            int columnWidth = 0;
+            for (int i = 0; i < columnCount; i++) {
+                minColumnWidth = Math.max(minColumnWidth, minWidths[i]);
+                columnWidth = Math.max(columnWidth, widths[i]);
+            }
+            columnWidth = width is DWT.DEFAULT || expandCount is 0 ? columnWidth
+                    : Math.max(minColumnWidth, availableWidth / columnCount);
+            for (int i = 0; i < columnCount; i++) {
+                expandColumn[i] = expandCount > 0;
+                widths[i] = columnWidth;
+            }
+        } else {
+            if (width !is DWT.DEFAULT && expandCount > 0) {
+                int totalWidth = 0;
+                for (int i = 0; i < columnCount; i++) {
+                    totalWidth += widths[i];
+                }
+                int count = expandCount;
+                int delta = (availableWidth - totalWidth) / count;
+                int remainder = (availableWidth - totalWidth) % count;
+                int last = -1;
+                while (totalWidth !is availableWidth) {
+                    for (int j = 0; j < columnCount; j++) {
+                        if (expandColumn[j]) {
+                            if (widths[j] + delta > minWidths[j]) {
+                                widths[last = j] = widths[j] + delta;
+                            } else {
+                                widths[j] = minWidths[j];
+                                expandColumn[j] = false;
+                                count--;
+                            }
+                        }
+                    }
+                    if (last > -1)
+                        widths[last] += remainder;
+
+                    for (int j = 0; j < columnCount; j++) {
+                        for (int i = 0; i < rowCount; i++) {
+                            GridData data = getData(grid, i, j, rowCount,
+                                    columnCount, false);
+                            if (data !is null) {
+                                int hSpan = Math.max(1, Math.min(
+                                        data.horizontalSpan, columnCount));
+                                if (hSpan > 1) {
+                                    if (data.widthHint !is DWT.DEFAULT
+                                            || !data.grabExcessHorizontalSpace) {
+                                        int spanWidth = 0, spanExpandCount = 0;
+                                        for (int k = 0; k < hSpan; k++) {
+                                            spanWidth += widths[j - k];
+                                            if (expandColumn[j - k])
+                                                spanExpandCount++;
+                                        }
+                                        int w = data.cacheWidth
+                                                + data.horizontalIndent
+                                                - spanWidth - (hSpan - 1)
+                                                * horizontalSpacing;
+                                        if (w > 0) {
+                                            if (spanExpandCount is 0) {
+                                                widths[j] += w;
+                                            } else {
+                                                int delta2 = w
+                                                        / spanExpandCount;
+                                                int remainder2 = w
+                                                        % spanExpandCount, last2 = -1;
+                                                for (int k = 0; k < hSpan; k++) {
+                                                    if (expandColumn[j - k]) {
+                                                        widths[last2 = j - k] += delta2;
+                                                    }
+                                                }
+                                                if (last2 > -1)
+                                                    widths[last2] += remainder2;
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    if (count is 0)
+                        break;
+                    totalWidth = 0;
+                    for (int i = 0; i < columnCount; i++) {
+                        totalWidth += widths[i];
+                    }
+                    delta = (availableWidth - totalWidth) / count;
+                    remainder = (availableWidth - totalWidth) % count;
+                    last = -1;
+                }
+            }
+        }
+
+        /* Wrapping */
+        GridData[] flush = null;
+        int flushLength = 0;
+        if (width !is DWT.DEFAULT) {
+            for (int j = 0; j < columnCount; j++) {
+                for (int i = 0; i < rowCount; i++) {
+                    GridData data = getData(grid, i, j, rowCount, columnCount,
+                            false);
+                    if (data !is null) {
+                        if (data.heightHint is DWT.DEFAULT) {
+                            IFigure child = grid[i][j];
+                            // TEMPORARY CODE
+                            int hSpan = Math.max(1, Math.min(
+                                    data.horizontalSpan, columnCount));
+                            int currentWidth = 0;
+                            for (int k = 0; k < hSpan; k++) {
+                                currentWidth += widths[j - k];
+                            }
+                            currentWidth += (hSpan - 1) * horizontalSpacing
+                                    - data.horizontalIndent;
+                            if ((currentWidth !is data.cacheWidth && data.horizontalAlignment is DWT.FILL)
+                                    || (data.cacheWidth > currentWidth)) {
+                                int trim = 0;
+                                /*
+                                 * // *Note*: Left this in place from DWT //
+                                 * GridLayout. Not sure if Draw2D Borders or //
+                                 * Scrollbars 'trim' will need to be takeninto
+                                 * account.
+                                 *
+                                 * if (child instanceof Group) { Group g
+                                 * =(Group)child; trim = g.getSize ().x -
+                                 * g.getClientArea ().width; } else if (child
+                                 * instanceof Scrollable) { Rectangle rect =
+                                 * ((Scrollable) child).computeTrim (0, 0, 0,0);
+                                 * trim = rect.width; } else { trim =
+                                 * child.getBorderWidth () * 2; }
+                                 */
+                                int oldWidthHint = data.widthHint;
+                                data.widthHint = Math.max(0, currentWidth
+                                        - trim);
+                                data.cacheWidth = data.cacheHeight = DWT.DEFAULT;
+                                data.computeSize(child, false);
+                                data.widthHint = oldWidthHint;
+                                if (flush is null)
+                                    flush = new GridData[children.size()];
+                                flush[flushLength++] = data;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        /* Row heights */
+        int availableHeight = height - verticalSpacing * (rowCount - 1)
+                - marginHeight * 2;
+        expandCount = 0;
+        int[] heights = new int[rowCount];
+        int[] minHeights = new int[rowCount];
+        bool[] expandRow = new bool[rowCount];
+        for (int i = 0; i < rowCount; i++) {
+            for (int j = 0; j < columnCount; j++) {
+                GridData data = getData(grid, i, j, rowCount, columnCount, true);
+                if (data !is null) {
+                    int vSpan = Math.max(1, Math.min(data.verticalSpan,
+                            rowCount));
+                    if (vSpan is 1) {
+                        int h = data.cacheHeight; // + data.verticalIndent;
+                        heights[i] = Math.max(heights[i], h);
+                        if (data.grabExcessVerticalSpace) {
+                            if (!expandRow[i])
+                                expandCount++;
+                            expandRow[i] = true;
+                        }
+                        if (data.heightHint !is DWT.DEFAULT
+                                || !data.grabExcessVerticalSpace) {
+                            minHeights[i] = Math.max(minHeights[i], h);
+                        }
+                    }
+                }
+            }
+            for (int j = 0; j < columnCount; j++) {
+                GridData data = getData(grid, i, j, rowCount, columnCount,
+                        false);
+                if (data !is null) {
+                    int vSpan = Math.max(1, Math.min(data.verticalSpan,
+                            rowCount));
+                    if (vSpan > 1) {
+                        int spanHeight = 0, spanMinHeight = 0, spanExpandCount = 0;
+                        for (int k = 0; k < vSpan; k++) {
+                            spanHeight += heights[i - k];
+                            spanMinHeight += minHeights[i - k];
+                            if (expandRow[i - k])
+                                spanExpandCount++;
+                        }
+                        if (data.grabExcessVerticalSpace
+                                && spanExpandCount is 0) {
+                            expandCount++;
+                            expandRow[i] = true;
+                        }
+                        int h = data.cacheHeight - spanHeight - (vSpan - 1)
+                                * verticalSpacing; // + data.verticalalIndent
+                        if (h > 0) {
+                            if (spanExpandCount is 0) {
+                                heights[i] += h;
+                            } else {
+                                int delta = h / spanExpandCount;
+                                int remainder = h % spanExpandCount, last = -1;
+                                for (int k = 0; k < vSpan; k++) {
+                                    if (expandRow[i - k]) {
+                                        heights[last = i - k] += delta;
+                                    }
+                                }
+                                if (last > -1)
+                                    heights[last] += remainder;
+                            }
+                        }
+                        if (data.heightHint !is DWT.DEFAULT
+                                || !data.grabExcessVerticalSpace) {
+                            h = data.cacheHeight - spanMinHeight - (vSpan - 1)
+                                    * verticalSpacing; // + data.verticalIndent
+                            if (h > 0) {
+                                if (spanExpandCount is 0) {
+                                    minHeights[i] += h;
+                                } else {
+                                    int delta = h / spanExpandCount;
+                                    int remainder = h % spanExpandCount, last = -1;
+                                    for (int k = 0; k < vSpan; k++) {
+                                        if (expandRow[i - k]) {
+                                            minHeights[last = i - k] += delta;
+                                        }
+                                    }
+                                    if (last > -1)
+                                        minHeights[last] += remainder;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        if (height !is DWT.DEFAULT && expandCount > 0) {
+            int totalHeight = 0;
+            for (int i = 0; i < rowCount; i++) {
+                totalHeight += heights[i];
+            }
+            int count = expandCount;
+            int delta = (availableHeight - totalHeight) / count;
+            int remainder = (availableHeight - totalHeight) % count;
+            int last = -1;
+            while (totalHeight !is availableHeight) {
+                for (int i = 0; i < rowCount; i++) {
+                    if (expandRow[i]) {
+                        if (heights[i] + delta > minHeights[i]) {
+                            heights[last = i] = heights[i] + delta;
+                        } else {
+                            heights[i] = minHeights[i];
+                            expandRow[i] = false;
+                            count--;
+                        }
+                    }
+                }
+                if (last > -1)
+                    heights[last] += remainder;
+
+                for (int i = 0; i < rowCount; i++) {
+                    for (int j = 0; j < columnCount; j++) {
+                        GridData data = getData(grid, i, j, rowCount,
+                                columnCount, false);
+                        if (data !is null) {
+                            int vSpan = Math.max(1, Math.min(data.verticalSpan,
+                                    rowCount));
+                            if (vSpan > 1) {
+                                if (data.heightHint !is DWT.DEFAULT
+                                        || !data.grabExcessVerticalSpace) {
+                                    int spanHeight = 0, spanExpandCount = 0;
+                                    for (int k = 0; k < vSpan; k++) {
+                                        spanHeight += heights[i - k];
+                                        if (expandRow[i - k])
+                                            spanExpandCount++;
+                                    }
+                                    int h = data.cacheHeight - spanHeight
+                                            - (vSpan - 1) * verticalSpacing; // +
+                                    // data.verticalIndent
+                                    if (h > 0) {
+                                        if (spanExpandCount is 0) {
+                                            heights[i] += h;
+                                        } else {
+                                            int delta2 = h / spanExpandCount;
+                                            int remainder2 = h
+                                                    % spanExpandCount, last2 = -1;
+                                            for (int k = 0; k < vSpan; k++) {
+                                                if (expandRow[i - k]) {
+                                                    heights[last2 = i - k] += delta2;
+                                                }
+                                            }
+                                            if (last2 > -1)
+                                                heights[last2] += remainder2;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+                if (count is 0)
+                    break;
+                totalHeight = 0;
+                for (int i = 0; i < rowCount; i++) {
+                    totalHeight += heights[i];
+                }
+                delta = (availableHeight - totalHeight) / count;
+                remainder = (availableHeight - totalHeight) % count;
+                last = -1;
+            }
+        }
+
+        /* Position the IFigures */
+        if (move) {
+            int gridY = y + marginHeight;
+            for (int i = 0; i < rowCount; i++) {
+                int gridX = x + marginWidth;
+                for (int j = 0; j < columnCount; j++) {
+                    GridData data = getData(grid, i, j, rowCount, columnCount,
+                            true);
+                    if (data !is null) {
+                        int hSpan = Math.max(1, Math.min(data.horizontalSpan,
+                                columnCount));
+                        int vSpan = Math.max(1, data.verticalSpan);
+                        int cellWidth = 0, cellHeight = 0;
+                        for (int k = 0; k < hSpan; k++) {
+                            cellWidth += widths[j + k];
+                        }
+                        for (int k = 0; k < vSpan; k++) {
+                            cellHeight += heights[i + k];
+                        }
+                        cellWidth += horizontalSpacing * (hSpan - 1);
+                        int childX = gridX + data.horizontalIndent;
+                        int childWidth = Math.min(data.cacheWidth, cellWidth);
+                        switch (data.horizontalAlignment) {
+                        case DWT.CENTER:
+                        case GridData.CENTER:
+                            childX = gridX
+                                    + Math.max(0, (cellWidth - childWidth) / 2);
+                            break;
+                        case DWT.RIGHT:
+                        case DWT.END:
+                        case GridData.END:
+                            childX = gridX
+                                    + Math.max(0, cellWidth - childWidth);
+                            break;
+                        case DWT.FILL:
+                            childWidth = cellWidth - data.horizontalIndent;
+                            break;
+                        }
+                        cellHeight += verticalSpacing * (vSpan - 1);
+                        int childY = gridY; // + data.verticalIndent;
+                        int childHeight = Math
+                                .min(data.cacheHeight, cellHeight);
+                        switch (data.verticalAlignment) {
+                        case DWT.CENTER:
+                        case GridData.CENTER:
+                            childY = gridY
+                                    + Math.max(0,
+                                            (cellHeight - childHeight) / 2);
+                            break;
+                        case DWT.BOTTOM:
+                        case DWT.END:
+                        case GridData.END:
+                            childY = gridY
+                                    + Math.max(0, cellHeight - childHeight);
+                            break;
+                        case DWT.FILL:
+                            childHeight = cellHeight; // -
+                            // data.verticalIndent;
+                            break;
+                        }
+                        IFigure child = grid[i][j];
+                        if (child !is null) {
+                            // following param could be replaced by
+                            // Rectangle.SINGLETON
+                            child.setBounds(new Rectangle(childX, childY,
+                                    childWidth, childHeight));
+                        }
+                    }
+                    gridX += widths[j] + horizontalSpacing;
+                }
+                gridY += heights[i] + verticalSpacing;
+            }
+        }
+
+        // clean up cache
+        for (int i = 0; i < flushLength; i++) {
+            flush[i].cacheWidth = flush[i].cacheHeight = -1;
+        }
+
+        int totalDefaultWidth = 0;
+        int totalDefaultHeight = 0;
+        for (int i = 0; i < columnCount; i++) {
+            totalDefaultWidth += widths[i];
+        }
+        for (int i = 0; i < rowCount; i++) {
+            totalDefaultHeight += heights[i];
+        }
+        totalDefaultWidth += horizontalSpacing * (columnCount - 1)
+                + marginWidth * 2;
+        totalDefaultHeight += verticalSpacing * (rowCount - 1) + marginHeight
+                * 2;
+        return new Dimension(totalDefaultWidth, totalDefaultHeight);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.draw2d.LayoutManager#getConstraint(dwtx.draw2d.IFigure)
+     */
+    public Object getConstraint(IFigure child) {
+        return constraints.get(cast(Object)child);
+    }
+
+    /**
+     * Sets the layout constraint of the given figure. The constraints can only
+     * be of type {@link GridData}.
+     *
+     * @see LayoutManager#setConstraint(IFigure, Object)
+     */
+    public void setConstraint(IFigure figure, Object newConstraint) {
+        super.setConstraint(figure, newConstraint);
+        if (newConstraint !is null) {
+            constraints.put(cast(Object)figure, newConstraint);
+
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/GroupBoxBorder.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.GroupBoxBorder;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.AbstractLabeledBorder;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.FigureUtilities;
+
+/**
+ * A labeled border intended to house a Figure with a group of children. The label should
+ * serve as a description of the group.
+ */
+public class GroupBoxBorder
+    : AbstractLabeledBorder
+{
+
+/**
+ * Constructs a GroupBoxBorder with the name of this class as its label.
+ *
+ * @since 2.0
+ */
+public this() { }
+
+/**
+ * Constructs a GroupBoxBorder with label s.
+ *
+ * @param s the label
+ * @since 2.0
+ */
+public this(String s) {
+    super(s);
+}
+
+/**
+ * Calculates and returns the Insets for this GroupBoxBorder.
+ *
+ * @param figure   IFigure on which the calculations should be made. Generally this is
+ *                  the IFigure of which this GroupBoxBorder is surrounding.
+ * @return  the Insets for this GroupBoxBorder.
+ * @since 2.0
+ */
+protected Insets calculateInsets(IFigure figure) {
+    int height = getTextExtents(figure).height;
+    return new Insets(height);
+}
+
+/**
+ * @see dwtx.draw2d.Border#getPreferredSize(IFigure)
+ */
+public Dimension getPreferredSize(IFigure fig) {
+    Dimension textSize = getTextExtents(fig);
+    return textSize.getCopy().expand(textSize.height * 2, 0);
+}
+
+/**
+ * @see Border#paint(IFigure, Graphics, Insets)
+ */
+public void paint(IFigure figure, Graphics g, Insets insets) {
+    tempRect.setBounds(getPaintRectangle(figure, insets));
+    Rectangle r = tempRect;
+    if (r.isEmpty())
+        return;
+
+    Rectangle textLoc = new Rectangle(r.getTopLeft(), getTextExtents(figure));
+    r.crop(new Insets(getTextExtents(figure).height / 2));
+    FigureUtilities.paintEtchedBorder(g, r);
+
+    textLoc.x += getInsets(figure).left;
+    g.setFont(getFont(figure));
+    g.setForegroundColor(getTextColor());
+    g.clipRect(textLoc);
+    g.fillText(getLabel(), textLoc.getTopLeft());
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/IFigure.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,911 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.IFigure;
+
+import dwt.dwthelper.utils;
+
+import dwtx.dwtxhelper.Bean;
+import dwtx.dwtxhelper.Collection;
+
+import dwt.graphics.Color;
+import dwt.graphics.Cursor;
+import dwt.graphics.Font;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.geometry.Translatable;
+
+import dwtx.draw2d.AncestorListener;
+import dwtx.draw2d.CoordinateListener;
+import dwtx.draw2d.FigureListener;
+import dwtx.draw2d.FocusListener;
+import dwtx.draw2d.KeyListener;
+import dwtx.draw2d.KeyEvent;
+import dwtx.draw2d.LayoutListener;
+import dwtx.draw2d.MouseListener;
+import dwtx.draw2d.MouseEvent;
+import dwtx.draw2d.MouseMotionListener;
+import dwtx.draw2d.TreeSearch;
+import dwtx.draw2d.Border;
+import dwtx.draw2d.LayoutManager;
+import dwtx.draw2d.FocusEvent;
+import dwtx.draw2d.UpdateManager;
+import dwtx.draw2d.EventDispatcher;
+import dwtx.draw2d.Graphics;
+
+/**
+ * Insets that are all 0.  Always returns <code>true<code> for {@link #isEmpty()}.
+ */
+class NoInsets
+    : Insets
+{
+    this() {
+        super(0, 0, 0, 0);
+    }
+    public bool isEmpty() {
+        return true;
+    }
+}
+
+static this(){
+    IFigure_MAX_DIMENSION = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
+    IFigure_MIN_DIMENSION = new Dimension(5, 5);
+    IFigure_NO_INSETS = new NoInsets();
+}
+/**
+ * The maximum allowable dimension. ({@link Integer#MAX_VALUE},{@link Integer#MAX_VALUE})
+ */
+static const Dimension IFigure_MAX_DIMENSION;
+
+/**
+ * The minimum allowable dimension. (5,5)
+ */
+static const Dimension IFigure_MIN_DIMENSION;
+
+/**
+ * Empty Insets.
+ */
+static const Insets IFigure_NO_INSETS;
+
+
+/**
+ * A lightweight graphical object.  Figures are rendered to a {@link Graphics}  object.
+ * Figures can be composed to create complex graphics.
+ *
+ * <P>
+ * WARNING: This interface is not intended to be implemented by clients.
+ */
+public interface IFigure {
+
+/**
+ * Adds the given IFigure as a child of this IFigure.  The same as calling {@link
+ * #add(IFigure, Object, int) add(figure, null, -1)}.
+ * @param figure The IFigure to add
+ */
+void add(IFigure figure);
+
+/**
+ * Adds the given IFigure as a child of this IFigure at the given index.  The same as
+ * calling {@link #add(IFigure, Object, int) add(figure, null, index)}.
+ * @param figure The IFigure to add
+ * @param index The index where the IFigure should be added
+ */
+void add(IFigure figure, int index);
+
+/**
+ * Adds the given IFigure as a child of this IFigure with the given constraint.  The same
+ * as calling {@link #add(IFigure, Object, int) add(figure, constraint, -1)}.
+ * @param figure The IFigure to add
+ * @param constraint The newly added IFigure's constraint
+ */
+void add(IFigure figure, Object constraint);
+
+/**
+ * Adds the child with the specified index and constraint. The child's parent is currently
+ * not null, it is removed from that parent.  If this figure has a LayoutManager, then
+ * {@link LayoutManager#setConstraint(IFigure, Object)} shall be called on the layout.
+ * @param figure The IFigure to add
+ * @param constraint The newly added IFigure's constraint
+ * @param index The index where the IFigure should be added
+ * @throws IndexOutOfBoundsException if the index is out of range
+ * @throws IllegalArgumentException if adding the child creates a cycle
+ */
+void add(IFigure figure, Object constraint, int index);
+
+/**
+ * Registers the given listener as an AncestorListener of this figure.
+ * @param listener The listener to add
+ */
+void addAncestorListener(AncestorListener listener);
+
+/**
+ * Registers the given listener as a CoordinateListener of this figure.
+ * @param listener the listener to add
+ * @since 3.1
+ */
+void addCoordinateListener(CoordinateListener listener);
+
+/**
+ * Registers the given listener as a FigureListener of this figure.
+ * @param listener The listener to add
+ */
+void addFigureListener(FigureListener listener);
+
+/**
+ * Registers the given listener as a FocusListener of this figure.
+ * @param listener The listener to add
+ */
+void addFocusListener(FocusListener listener);
+
+/**
+ * Registers the given listener as a KeyListener of this figure.
+ * @param listener The listener to add
+ */
+void addKeyListener(KeyListener listener);
+
+/**
+ * Registers the given listener on this figure.
+ * @param listener The listener to add
+ * @since 3.1
+ */
+void addLayoutListener(LayoutListener listener);
+
+/**
+ * Registers the given listener as a MouseListener of this IFigure.
+ * @param listener The listener to add
+ */
+void addMouseListener(MouseListener listener);
+
+/**
+ * Registers the given listener as a MouseMotionListener of this IFigure.
+ * @param listener The listener to add
+ */
+void addMouseMotionListener(MouseMotionListener listener);
+
+/**
+ * Called after this IFigure is added to its parent.
+ */
+void addNotify();
+
+/**
+ * Registers the given listener as a PropertyChangeListener of this IFigure.
+ * @param listener The listener to add
+ */
+void addPropertyChangeListener(PropertyChangeListener listener);
+
+/**
+ * Registers the given listener as a PropertyChangeListener of this IFigure, interested
+ * only in the given property.
+ * @param property The property the listener is interested in
+ * @param listener The listener to add
+ */
+void addPropertyChangeListener(String property, PropertyChangeListener listener);
+
+/**
+ * Returns <code>true</code> if the point <code>(x, y)</code> is contained within this
+ * IFigure's bounds.
+ * @param x The X coordinate
+ * @param y The Y coordinate
+ * @return <code>true</code> if the point (x,y) is contained in this IFigure's bounds
+ */
+bool containsPoint(int x, int y);
+
+/**
+ * Returns <code>true</code> if the Point p is contained within this IFigure's bounds.
+ * @param p The point
+ * @return <code>true</code> if the Point p is contained within this IFigure's bounds
+ */
+bool containsPoint(Point p);
+
+/**
+ * Erases this IFigure.
+ */
+void erase();
+
+/**
+ * Returns the IFigure at the specified location. May return <code>this</code> or
+ * <code>null</code>.
+ * @param x The X coordinate
+ * @param y The Y coordinate
+ * @return The IFigure at the specified location
+ */
+IFigure findFigureAt(int x, int y);
+
+/**
+ * Returns the IFigure at the specified location based on the conditional TreeSearch.  May
+ * return <code>this</code> or <code>null</code>
+ * @param x the X coordinate
+ * @param y the Y coordinate
+ * @param search the conditional TreeSearch
+ * @return the IFigure at the specified location
+ */
+IFigure findFigureAt(int x, int y, TreeSearch search);
+
+/**
+ * Returns the IFigure at the specified location. May return <code>this</code> or
+ * <code>null</code>.
+ * @param p The point
+ * @return The IFigure at the specified location
+ */
+IFigure findFigureAt(Point p);
+
+/**
+ * Returns the IFigure at the specified location, excluding any IFigures in
+ * <code>collection</code>. May return <code>this</code> or <code>null</code>.
+ * @param x The X coordinate
+ * @param y The Y coordinate
+ * @param collection A collection of IFigures to be excluded
+ * @return The IFigure at the specified location, excluding any IFigures in collection
+ */
+IFigure findFigureAtExcluding(int x, int y, Collection collection);
+
+/**
+ * Returns the IFigure located at the given location which will accept mouse events.
+ * @param x The X coordinate
+ * @param y The Y coordinate
+ * @return The IFigure located at the given location which will accept mouse events
+ */
+IFigure findMouseEventTargetAt(int x, int y);
+
+/**
+ * Returns the background color. Background color can be inherited from the parent.
+ * @return The background color
+ */
+Color getBackgroundColor();
+
+/**
+ * Returns the current border by reference.
+ * @return The current border
+ */
+Border getBorder();
+
+/**
+ * Returns the smallest rectangle completely enclosing the IFigure. Implementation may
+ * return the Rectangle by reference. For this reason, callers of this method must not
+ * modify the returned Rectangle. The Rectangle's values may change in the future.
+ * @return This IFigure's bounds
+ */
+Rectangle getBounds();
+
+/**
+ * Returns an unmodifiable list of children by reference.
+ * @return An unmodifiable list of children by reference
+ */
+List getChildren();
+
+/**
+ * Returns the rectangular area within this Figure's bounds in which children will be
+ * placed (via {@link LayoutManager LayoutManagers}) and the painting of children will be
+ * clipped.
+ * @return The client area
+ */
+Rectangle getClientArea();
+
+/**
+ * Copies the client area into the specificied Recangle, and returns that rectangle for
+ * convenience.
+ * @param rect The destination rectangle for the client area
+ * @return The same instance that was passed in, modified to contain the client area
+ */
+Rectangle getClientArea(Rectangle rect);
+
+/**
+ * Returns the Cursor used when the mouse is over this IFigure.
+ * @return The Cursor used when the mouse is over this IFigure
+ */
+Cursor getCursor();
+
+/**
+ * Returns the current Font by reference.
+ * @return The current Font
+ */
+Font getFont();
+
+/**
+ * Returns the foreground color.
+ * @return The foreground color
+ */
+Color getForegroundColor();
+
+/**
+ * Returns the current Insets.  May be returned by reference.  The returned value should
+ * not be modified.
+ * @return The current Insets.
+ */
+Insets getInsets();
+
+/**
+ * Returns the current LayoutManager by reference.
+ * @return The current LayoutManager by reference
+ */
+LayoutManager getLayoutManager();
+
+/**
+ * Returns the background Color of this Figure. Does not inherit this Color from the
+ * parent, may return null.
+ * @return The local background Color
+ */
+Color getLocalBackgroundColor();
+
+/**
+ * Returns the local foreground Color of this Figure. Does not inherit this Color from the
+ * parent, may return null.
+ * @return The local foreground Color
+ */
+Color getLocalForegroundColor();
+
+/**
+ * Returns a hint indicating the largest desireable size for the IFigure. Returned
+ * Dimension is by value.
+ * @return The maximum size
+ */
+Dimension getMaximumSize();
+
+/**
+ * Returns a hint indicating the smallest desireable size for the IFigure.
+ * The returned dimension may be by <i>reference</i>, and it must not be modified by the
+ * caller.
+ * @return The minimum size
+ */
+Dimension getMinimumSize();
+
+/**
+ * Returns a hint indicating the smallest desireable size for the IFigure.
+ * The returned dimension may be by <i>reference</i>, and it must not be modified by the
+ * caller.
+ * @param wHint the width hint
+ * @param hHint the height hint
+ * @return The minimum size
+ */
+Dimension getMinimumSize(int wHint, int hHint);
+
+/**
+ * Returns the IFigure that is the current parent of this IFigure or <code>null</code> if
+ * there is no parent.
+ * @return <code>null</code> or the parent figure
+ */
+IFigure getParent();
+
+/**
+ * Returns the preferred size for this IFigure. The returned value must not be modified
+ * by the caller.  If the figure has no preference, it returns its current size.  The same
+ * as calling {@link #getPreferredSize(int, int) getPreferredSize(-1, -1)}.
+ * @return The preferred size
+ */
+Dimension getPreferredSize();
+
+/**
+ * Returns the preferred size for this IFigure using the provided width and height hints.
+ * The returned dimension may be by <i>reference</i>, and it must not be modified by the
+ * caller.  A value of <code>-1</code> indicates that there is no constraint in that
+ * direction.
+ *
+ * @param wHint a width hint
+ * @param hHint a height hint
+ * @return The preferred size
+ */
+Dimension getPreferredSize(int wHint, int hHint);
+
+/**
+ * Returns the current size. Returned Dimension is by value.
+ * @return The current size
+ */
+Dimension getSize();
+
+/**
+ * Returns a IFigure that is the tooltip for this IFigure.
+ * @return This IFigure's tooltip
+ */
+IFigure getToolTip();
+
+/**
+ * Returns the UpdateManager for this IFigure by reference.
+ * @return The update manager
+ */
+UpdateManager getUpdateManager();
+
+/**
+ * Called when this IFigure has gained focus.
+ * <p><b>NOTE</b>: You should not override this method. If you are interested in receiving
+ * notification of this type of event, you should register a {@link FocusListener} with
+ * this IFigure.
+ * @param event The focus event
+ */
+void handleFocusGained(FocusEvent event);
+
+/**
+ * Called when this IFigure has lost focus.
+ * <p><b>NOTE</b>: You should not override this method. If you are interested in receiving
+ * notification of this type of event, you should register a {@link FocusListener} with
+ * this IFigure.
+ * @param event The focus event
+ */
+void handleFocusLost(FocusEvent event);
+
+/**
+ * Called when a key is pressed while this IFigure has focus.
+ * <p><b>NOTE</b>: You should not override this method. If you are interested in receiving
+ * notification of this type of event, you should register a {@link KeyListener} with
+ * this IFigure.
+ * @param event The key event
+ */
+void handleKeyPressed(KeyEvent event);
+
+/**
+ * Called when a key is released while this IFigure has focus.
+ * <p><b>NOTE</b>: You should not override this method. If you are interested in receiving
+ * notification of this type of event, you should register a {@link KeyListener} with
+ * this IFigure.
+ * @param event The key event
+ */
+void handleKeyReleased(KeyEvent event);
+
+/**
+ * Called when a mouse button has been double-clicked while within this IFigure's bounds.
+ * <p><b>NOTE</b>: You should not override this method. If you are interested in receiving
+ * notification of this type of event, you should register a {@link MouseListener} with
+ * this IFigure.
+ * @param event The mouse event
+ */
+void handleMouseDoubleClicked(MouseEvent event);
+
+/**
+ * Called when the mouse has been dragged within this IFigure's bounds.
+ * <p><b>NOTE</b>: You should not override this method. If you are interested in receiving
+ * notification of this type of event, you should register a {@link MouseMotionListener}
+ * with this IFigure.
+ * @param event The mouse event
+ */
+void handleMouseDragged(MouseEvent event);
+
+/**
+ * Called when the mouse has entered this IFigure's bounds.
+ * <p><b>NOTE</b>: You should not override this method. If you are interested in receiving
+ * notification of this type of event, you should register a {@link MouseMotionListener}
+ * with this IFigure.
+ * @param event The mouse event
+ */
+void handleMouseEntered(MouseEvent event);
+
+/**
+ * Called when the mouse has exited this IFigure's bounds.
+ * <p><b>NOTE</b>: You should not override this method. If you are interested in receiving
+ * notification of this type of event, you should register a {@link MouseMotionListener}
+ * with this IFigure.
+ * @param event The mouse event
+ */
+void handleMouseExited(MouseEvent event);
+
+/**
+ * Called when the mouse has hovered over this IFigure.
+ * <p><b>NOTE</b>: You should not override this method. If you are interested in receiving
+ * notification of this type of event, you should register a {@link MouseMotionListener}
+ * with this IFigure.
+ * @param event The mouse event
+ */
+void handleMouseHover(MouseEvent event);
+
+/**
+ * Called when the mouse has moved within this IFigure's bounds.
+ * <p><b>NOTE</b>: You should not override this method. If you are interested in receiving
+ * notification of this type of event, you should register a {@link MouseMotionListener}
+ * with this IFigure.
+ * @param event The mouse event
+ */
+void handleMouseMoved(MouseEvent event);
+
+/**
+ * Called when a mouse button has been pressed while within this IFigure's bounds.
+ * <p><b>NOTE</b>: You should not override this method. If you are interested in receiving
+ * notification of this type of event, you should register a {@link MouseListener} with
+ * this IFigure.
+ * @param event The mouse event
+ */
+void handleMousePressed(MouseEvent event);
+
+/**
+ * Called when a mouse button has been released while within this IFigure's bounds.
+ * <p><b>NOTE</b>: You should not override this method. If you are interested in receiving
+ * notification of this type of event, you should register a {@link MouseListener} with
+ * this IFigure.
+ * @param event The mouse event
+ */
+void handleMouseReleased(MouseEvent event);
+
+/**
+ * Returns <code>true</code> if this IFigure has focus.
+ * @return <code>true</code> if this IFigure has focus
+ */
+bool hasFocus();
+
+/**
+ * This method is <b>for internal purposes only</b> and should not be called.
+ * @return The event dispatcher
+ */
+EventDispatcher internalGetEventDispatcher();
+
+/**
+ * Returns <code>true</code> if this IFigure's bounds intersect with the given Rectangle.
+ * Figure is asked so that non-rectangular IFigures can reduce the frequency of paints.
+ * @param rect The rectangle to check for intersection
+ * @return <code>true</code> if this IFigure's bounds intersect with the given Rectangle
+ */
+bool intersects(Rectangle rect);
+
+/**
+ * Invalidates this IFigure.  If this figure has a LayoutManager, then
+ * {@link LayoutManager#invalidate()} should be called on that layout.
+ */
+void invalidate();
+
+/**
+ * Invalidates this figure as well as all contained within.
+ */
+void invalidateTree();
+
+/**
+ * Returns <code>true</code> if this figure is capable of applying a local coordinate
+ * system which affects its children.
+ * @since 3.1
+ * @return <code>true</code> if this figure provides local coordinates to children
+ */
+bool isCoordinateSystem();
+
+/**
+ * Returns <code>true</code> if this IFigure is enabled.
+ * @return <code>true</code> if this IFigure is enabled
+ */
+bool isEnabled();
+
+/**
+ * Returns <code>true</code> if this IFigure can gain focus on a
+ * {@link dwt.events.TraverseEvent}.
+ * @return <code>true</code> if this IFigure can gain focus on a TraverseEvent
+ */
+bool isFocusTraversable();
+
+/**
+ * @return <code>true</code> if this figure is hosted in a Control that is mirrored
+ * @since 3.1
+ */
+bool isMirrored();
+
+/**
+ * Returns <code>true</code> if this IFigure is opaque.
+ * @return <code>true</code> if this IFigure is opaque
+ */
+bool isOpaque();
+
+/**
+ * Returns <code>true</code> if this IFigure can receive focus on a call to
+ * {@link #requestFocus()}.
+ * @return <code>true</code> if this IFigure can receive focus on a call to requestFocus()
+ */
+bool isRequestFocusEnabled();
+
+/**
+ * Returns <code>true</code> if this IFigure is showing. This figure is only showing if
+ * it is visible and its parent is showing, or it has no parent.
+ * @return <code>true</code> if this IFigure is showing
+ */
+bool isShowing();
+
+/**
+ * returns <code>true</code> if this figure's visibility flag is set to true. Does not
+ * walk up the parent chain.
+ * @return <code>true</code> if the figure's visibility flag is set
+ */
+bool isVisible();
+
+/**
+ * Paints this IFigure and its children.
+ * @param graphics The Graphics object used for painting
+ */
+void paint(Graphics graphics);
+
+/**
+ * Removes the given child from this figure's children.  If this figure has a
+ * LayoutManager, then {@link LayoutManager#remove(IFigure)} shall be called on that
+ * layout with the child.
+ * @param figure The IFigure to remove
+ */
+void remove(IFigure figure);
+
+/**
+ * Unregisters the given listener, so that it will no longer receive notification of
+ * ancestor events.
+ * @param listener The listener to remove
+ */
+void removeAncestorListener(AncestorListener listener);
+
+/**
+ * Unregisters the given listener, so that it will no longer receive notification of
+ * coordinate changes.
+ * @param listener the listener to remove
+ * @since 3.1
+ */
+void removeCoordinateListener(CoordinateListener listener);
+
+/**
+ * Unregisters the given listener, so that it will no longer receive notification of
+ * IFigure events.
+ * @param listener The listener to remove
+ */
+void removeFigureListener(FigureListener listener);
+
+/**
+ * Unregisters the given listener, so that it will no longer receive notification of focus
+ * events.
+ * @param listener The listener to remove
+ */
+void removeFocusListener(FocusListener listener);
+
+/**
+ * Removes the first occurence of the given listener.
+ * @param listener The listener to remove
+ */
+void removeKeyListener(KeyListener listener);
+
+/**
+ * Removes the most recent occurence of the given listener.
+ * @since 3.1
+ * @param listener the listener to remove
+ */
+void removeLayoutListener(LayoutListener listener);
+
+/**
+ * Unregisters the given listener, so that it will no longer receive notification of mouse
+ * events.
+ * @param listener The listener to remove
+ */
+void removeMouseListener(MouseListener listener);
+
+/**
+ * Unregisters the given listener, so that it will no longer receive notification of mouse
+ * motion events.
+ * @param listener The listener to remove
+ */
+void removeMouseMotionListener(MouseMotionListener listener);
+
+/**
+ * Called before this IFigure is removed from its parent.
+ */
+void removeNotify();
+
+/**
+ * Unregisters the given listener, so that it will no longer receive notification of any
+ * property changes.
+ * @param listener The listener to remove
+ */
+void removePropertyChangeListener(PropertyChangeListener listener);
+
+/**
+ * Unregisters the given listener, so that it will no longer receive notification of
+ * changes in the given property. This will only unregister the listener for the given
+ * property. If the listener is registered to listen to other properties, this will not
+ * affect the notification of the listener regarding those properties.
+ * @param property The property that the listener is no longer interested in
+ * @param listener The listener no longer interested in the property
+ */
+void removePropertyChangeListener(String property, PropertyChangeListener listener);
+
+/**
+ * Repaints this IFigure.
+ */
+void repaint();
+
+/**
+ * Repaints the rectangular area within this IFigure whose upper-left corner is located at
+ * the point <code>(x,y)</code> and whose width and height are <code>w</code> and
+ * <code>h</code>, respectively.
+ * @param x The X coordinate of the area to repaint
+ * @param y The Y coordinate of the area to repaint
+ * @param w The width of the area to repaint
+ * @param h The height of the area to repaint
+ */
+void repaint(int x, int y, int w, int h);
+
+/**
+ * Repaints the rectangular area within this IFigure represented by <code>rect</code>.
+ * @param rect The rectangular area to be repainted
+ */
+void repaint(Rectangle rect);
+
+/**
+ * Requests focus from the {@link EventDispatcher}.
+ */
+void requestFocus();
+
+/**
+ * Invalidates this figure and revalidates() its parent.  If a figure does not have a
+ * parent, it will request a validation from it UpdateManager. Calling this method does
+ * not guarantee that a repaint will occur.
+ */
+void revalidate();
+
+/**
+ * Sets the background color.
+ * @param c The new background color
+ */
+void setBackgroundColor(Color c);
+
+/**
+ * Sets the border.
+ * @param b The new border
+ */
+void setBorder(Border b);
+
+/**
+ * Sets the bounds to the bounds of the specified <code>Rectangle</code>.
+ * @param rect The new bounds
+ */
+void setBounds(Rectangle rect);
+
+/**
+ * Convenience method to set the constraint of the specified child in the current
+ * LayoutManager.
+ * @param child The figure whose constraint is being set
+ * @param constraint the constraint
+ * @throws IllegalArgumentException if the child is not contained by this Figure
+ */
+void setConstraint(IFigure child, Object constraint);
+
+/**
+ * Sets the cursor.
+ * @param cursor The new cursor
+ */
+void setCursor(Cursor cursor);
+
+/**
+ * Sets this IFigure to be enabled.
+ * @param value <code>true</code> if this IFigure should be enabled
+ */
+void setEnabled(bool value);
+
+/**
+ * Sets the ability for this IFigure to gain focus on a
+ * {@link dwt.events.TraverseEvent}.
+ * @param value <code>true</code> if this IFigure should gain focus on a TraverseEvent
+ */
+void setFocusTraversable(bool value);
+
+/**
+ * Sets the font.
+ * @param f The new font
+ */
+void setFont(Font f);
+
+/**
+ * Sets the foreground color.
+ * @param c The new foreground color
+ */
+void setForegroundColor(Color c);
+
+/**
+ * Sets the LayoutManager.
+ * @param lm The new layout manager
+ */
+void setLayoutManager(LayoutManager lm);
+
+/**
+ * Sets the location of this IFigure.
+ * @param p The new location
+ */
+void setLocation(Point p);
+
+/**
+ * Sets the maximum size this IFigure can be.
+ * @param size The new maximum size
+ */
+void setMaximumSize(Dimension size);
+
+/**
+ * Sets the minimum size this IFigure can be.
+ * @param size The new minimum size
+ */
+void setMinimumSize(Dimension size);
+
+/**
+ * Sets this IFigure to be opaque if <i>isOpaque</i> is <code>true</code> and transparent
+ * if <i>isOpaque</i> is <code>false</code>.
+ * @param isOpaque <code>true</code> is this IFigure should be opaque
+ */
+void setOpaque(bool isOpaque);
+
+/**
+ * Sets this IFigure's parent.
+ * @param parent The new parent IFigure
+ */
+void setParent(IFigure parent);
+
+/**
+ * Sets this IFigure's preferred size.
+ * @param size The new preferred size
+ */
+void setPreferredSize(Dimension size);
+
+/**
+ * Sets the ability for this Figure to gain focus on a call to {@link #requestFocus()}.
+ * @param requestFocusEnabled <code>true</code> if this IFigure should gain focus on a call
+ * to requestFocus()
+ */
+void setRequestFocusEnabled(bool requestFocusEnabled);
+
+/**
+ * Sets this IFigure's size.
+ * @param d The new size
+ */
+void setSize(Dimension d);
+
+/**
+ * Sets this IFigure's size.
+ * @param w The new width
+ * @param h The new height
+ */
+void setSize(int w, int h);
+
+/**
+ * Sets a tooltip that is displayed when the mouse hovers over this IFigure.
+ * @param figure The tooltip IFigure
+ */
+void setToolTip(IFigure figure);
+
+/**
+ * Sets this IFigure's visibility.
+ * @param visible <code>true</code> if this IFigure should be visible
+ */
+void setVisible(bool visible);
+
+/**
+ * Moves this IFigure <code>x</code> pixels horizontally and <code>y</code> pixels
+ * vertically.
+ * @param x The amount to move this IFigure horizontally
+ * @param y The amount to move this IFigure vertically
+ */
+void translate(int x, int y);
+
+/**
+ * Translates a Translatable from this IFigure's parent's coordinates to this IFigure's
+ * local coordinates.
+ * @param t The object to translate
+ */
+void translateFromParent(Translatable t);
+
+/**
+ * Translates a Translatable that is relative to this figure's bounds to absolute.
+ * @param t The object to translate
+ */
+void translateToAbsolute(Translatable t);
+
+/**
+ * Translates a Translatable from this IFigure's coordinates to its parent's coordinates.
+ * @param t The object to translate
+ */
+void translateToParent(Translatable t);
+
+/**
+ * Translates a Translatable in absolute coordinates to be relative to this figure's
+ * bounds.
+ * @param t The object to translate
+ */
+void translateToRelative(Translatable t);
+
+/**
+ * Indicates that this figure should make itself valid.  Validation includes invoking
+ * layout on a LayoutManager if present, and then validating all children figures.
+ * Default validation uses pre-order, depth-first ordering.
+ */
+void validate();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ImageFigure.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,172 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ImageFigure;
+
+import dwt.dwthelper.utils;
+
+
+
+import dwt.graphics.Image;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Figure;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.PositionConstants;
+
+/**
+ * A Figure that simply contains an Image.  Use this Figure, instead of a Label, when
+ * displaying Images without any accompanying text.  This figure is not intended to have a
+ * layout mananger or children.
+ * <P>
+ * Note that it is the client's responsibility to dispose the given image.  There is no
+ * "free" resource management in draw2d.
+ *
+ * @author Pratik Shah
+ */
+public class ImageFigure
+    : Figure
+{
+
+private Image img;
+private Dimension size;
+private int alignment;
+
+/**
+ * Constructor<br>
+ * The default alignment is <code>PositionConstants.CENTER</code>.
+ */
+public this() {
+    this(null, PositionConstants.CENTER);
+}
+
+/**
+ * Constructor<br>
+ * The default alignment is <code>PositionConstants.CENTER</code>.
+ *
+ * @param   image   The Image to be displayed
+ */
+public this(Image image) {
+    this(image, PositionConstants.CENTER);
+}
+
+/**
+ * Constructor
+ *
+ * @param   image       The Image to be displayed
+ * @param   alignment   A PositionConstant indicating the alignment
+ *
+ * @see ImageFigure#setImage(Image)
+ * @see ImageFigure#setAlignment(int)
+ */
+public this(Image image, int alignment) {
+    size = new Dimension();
+    setImage(image);
+    setAlignment(alignment);
+}
+
+/**
+ * @return The Image that this Figure displays
+ */
+public Image getImage() {
+    return img;
+}
+
+/**
+ * Calculates the necessary size to display the Image within the figure's client area.
+ *
+ * @see dwtx.draw2d.Figure#getPreferredSize(int, int)
+ */
+public Dimension getPreferredSize(int wHint, int hHint) {
+    if (getInsets() is IFigure_NO_INSETS)
+        return size;
+    Insets i = getInsets();
+    return size.getExpanded(i.getWidth(), i.getHeight());
+}
+
+/**
+ * @see dwtx.draw2d.Figure#paintFigure(Graphics)
+ */
+protected void paintFigure(Graphics graphics) {
+    super.paintFigure(graphics);
+
+    if (getImage() is null)
+        return;
+
+    int x, y;
+    Rectangle area = getClientArea();
+    switch (alignment & PositionConstants.NORTH_SOUTH) {
+        case PositionConstants.NORTH:
+            y = area.y;
+            break;
+        case PositionConstants.SOUTH:
+            y = area.y + area.height - size.height;
+            break;
+        default:
+            y = (area.height - size.height) / 2 + area.y;
+            break;
+    }
+    switch (alignment & PositionConstants.EAST_WEST) {
+        case PositionConstants.EAST:
+            x = area.x + area.width - size.width;
+            break;
+        case PositionConstants.WEST:
+            x = area.x;
+            break;
+        default:
+            x = (area.width - size.width) / 2 + area.x;
+            break;
+    }
+    graphics.drawImage(getImage(), x, y);
+}
+
+/**
+ * Sets the alignment of the Image within this Figure.  The alignment comes into play
+ * when the ImageFigure is larger than the Image.  The alignment could be any valid
+ * combination of the following:
+ *
+ * <UL>
+ *      <LI>PositionConstants.NORTH</LI>
+ *      <LI>PositionConstants.SOUTH</LI>
+ *      <LI>PositionConstants.EAST</LI>
+ *      <LI>PositionConstants.WEST</LI>
+ *      <LI>PositionConstants.CENTER or PositionConstants.NONE</LI>
+ * </UL>
+ *
+ * @param flag A constant indicating the alignment
+ */
+public void setAlignment(int flag) {
+    alignment = flag;
+}
+
+/**
+ * Sets the Image that this ImageFigure displays.
+ * <p>
+ * IMPORTANT: Note that it is the client's responsibility to dispose the given image.
+ *
+ * @param image The Image to be displayed.  It can be <code>null</code>.
+ */
+public void setImage(Image image) {
+    if (img is image)
+        return;
+    img = image;
+    if (img !is null)
+        size = (new Rectangle(image.getBounds())).getSize();
+    else
+        size = new Dimension();
+    revalidate();
+    repaint();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ImageUtilities.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,187 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ImageUtilities;
+
+import dwt.dwthelper.utils;
+
+
+static import dwt.graphics.Rectangle;
+import dwt.DWT;
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.FontMetrics;
+import dwt.graphics.GC;
+import dwt.graphics.Image;
+import dwt.graphics.ImageData;
+import dwt.graphics.PaletteData;
+import dwt.graphics.RGB;
+import dwt.widgets.Display;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.FigureUtilities;
+
+/**
+ * @author Pratik Shah
+ * @since 3.0
+ */
+public class ImageUtilities {
+
+/**
+ * Returns a new Image with the given String rotated left (by 90 degrees).  The String
+ * will be rendered using the provided colors and fonts.  The client is responsible for
+ * disposing the returned Image.  Strings cannot contain newline or tab characters.  This
+ * method MUST be invoked from the user-interface (Display) thread.
+ *
+ * @param string the String to be rendered
+ * @param font the font
+ * @param foreground the text's color
+ * @param background the background color
+ * @return an Image which must be disposed
+ */
+public static Image createRotatedImageOfString(String string, Font font,
+                                               Color foreground, Color background) {
+    Display display = Display.getCurrent();
+    if (display is null)
+        DWT.error(DWT.ERROR_THREAD_INVALID_ACCESS);
+
+    FontMetrics metrics = FigureUtilities.getFontMetrics(font);
+    Dimension strSize = FigureUtilities.getStringExtents(string, font);
+    Image srcImage = new Image(display, strSize.width, metrics.getAscent());
+    GC gc = new GC(srcImage);
+    gc.setFont(font);
+    gc.setForeground(foreground);
+    gc.setBackground(background);
+    gc.fillRectangle(srcImage.getBounds());
+    gc.drawString(string, 0, 0 - metrics.getLeading());
+    Image result = createRotatedImage(srcImage);
+    gc.dispose();
+    srcImage.dispose();
+    return result;
+}
+
+/**
+ * Returns a new Image that is the given Image rotated left by 90 degrees.  The client is
+ * responsible for disposing the returned Image.  This method MUST be invoked from the
+ * user-interface (Display) thread.
+ *
+ * @param   srcImage    the Image that is to be rotated left
+ * @return  the rotated Image (the client is responsible for disposing it)
+ */
+public static Image createRotatedImage(Image srcImage) {
+    Display display = Display.getCurrent();
+    if (display is null)
+        DWT.error(DWT.ERROR_THREAD_INVALID_ACCESS);
+
+    ImageData srcData = srcImage.getImageData();
+    ImageData destData;
+    if (srcData.depth < 8)
+        destData = rotatePixelByPixel(srcData);
+    else
+        destData = rotateOptimized(srcData);
+
+    return new Image(display, destData);
+}
+
+/**
+ * Creates an ImageData representing the given <code>Image</code> shaded with the given
+ * <code>Color</code>.
+ *
+ * @param fromImage Image that has to be shaded
+ * @param shade     The Color to be used for shading
+ * @return A new ImageData that can be used to create an Image.
+ */
+public static ImageData createShadedImage(Image fromImage, Color shade) {
+    dwt.graphics.Rectangle.Rectangle r = fromImage.getBounds();
+    ImageData data = fromImage.getImageData();
+    PaletteData palette = data.palette;
+    if (!palette.isDirect) {
+        /* Convert the palette entries */
+        RGB [] rgbs = palette.getRGBs();
+        for (int i = 0; i < rgbs.length; i++) {
+            if (data.transparentPixel !is i) {
+                RGB color = rgbs [i];
+                color.red = determineShading(color.red, shade.getRed());
+                color.blue = determineShading(color.blue, shade.getBlue());
+                color.green = determineShading(color.green, shade.getGreen());
+            }
+        }
+        data.palette = new PaletteData(rgbs);
+    } else {
+        /* Convert the pixels. */
+        int[] scanline = new int[r.width];
+        int redMask = palette.redMask;
+        int greenMask = palette.greenMask;
+        int blueMask = palette.blueMask;
+        int redShift = palette.redShift;
+        int greenShift = palette.greenShift;
+        int blueShift = palette.blueShift;
+        for (int y = 0; y < r.height; y++) {
+            data.getPixels(0, y, r.width, scanline, 0);
+            for (int x = 0; x < r.width; x++) {
+                int pixel = scanline[x];
+                int red = pixel & redMask;
+                red = (redShift < 0) ? red >>> -redShift : red << redShift;
+                int green = pixel & greenMask;
+                green = (greenShift < 0) ? green >>> -greenShift : green << greenShift;
+                int blue = pixel & blueMask;
+                blue = (blueShift < 0) ? blue >>> -blueShift : blue << blueShift;
+                red = determineShading(red, shade.getRed());
+                blue = determineShading(blue, shade.getBlue());
+                green = determineShading(green, shade.getGreen());
+                red = (redShift < 0) ? red << -redShift : red >> redShift;
+                red &= redMask;
+                green = (greenShift < 0) ? green << -greenShift : green >> greenShift;
+                green &= greenMask;
+                blue = (blueShift < 0) ? blue << -blueShift : blue >> blueShift;
+                blue &= blueMask;
+                scanline[x] = red | blue | green;
+            }
+            data.setPixels(0, y, r.width, scanline, 0);
+        }
+    }
+    return data;
+}
+
+private static int determineShading(int origColor, int shadeColor) {
+    return (origColor + shadeColor) / 2;
+}
+
+private static ImageData rotateOptimized(ImageData srcData) {
+    int bytesPerPixel = Math.max(1, srcData.depth / 8);
+    int destBytesPerLine = ((srcData.height * bytesPerPixel - 1) / srcData.scanlinePad + 1)
+            * srcData.scanlinePad;
+    byte[] newData = new byte[destBytesPerLine * srcData.width];
+    for (int srcY = 0; srcY < srcData.height; srcY++) {
+        for (int srcX = 0; srcX < srcData.width; srcX++) {
+            int destX = srcY;
+            int destY = srcData.width - srcX - 1;
+            int destIndex = (destY * destBytesPerLine) + (destX * bytesPerPixel);
+            int srcIndex = (srcY * srcData.bytesPerLine) + (srcX * bytesPerPixel);
+            System.arraycopy(srcData.data, srcIndex, newData, destIndex, bytesPerPixel);
+        }
+    }
+    return new ImageData(srcData.height, srcData.width, srcData.depth, srcData.palette,
+            srcData.scanlinePad, newData);
+}
+
+private static ImageData rotatePixelByPixel(ImageData srcData) {
+    ImageData destData = new ImageData(srcData.height, srcData.width, srcData.depth,
+            srcData.palette);
+    for (int y = 0; y < srcData.height; y++) {
+        for (int x = 0; x < srcData.width; x++) {
+            destData.setPixel(y, srcData.width - x - 1, srcData.getPixel(x, y));
+        }
+    }
+    return destData;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/InputEvent.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.InputEvent;
+
+import dwt.dwthelper.utils;
+
+import dwt.DWT;
+import dwtx.draw2d.EventDispatcher;
+import dwtx.draw2d.IFigure;
+
+/**
+ * The base class for Draw2d events.
+ */
+public abstract class InputEvent
+    : /+java.util.+/EventObject
+{
+
+private int state;
+
+private bool consumed = false;
+
+/** @see DWT#ALT */
+public static const int ALT = DWT.ALT;
+/** @see DWT#CONTROL */
+public static const int CONTROL = DWT.CONTROL;
+/** @see DWT#SHIFT */
+public static const int SHIFT = DWT.SHIFT;
+/** @see DWT#BUTTON1 */
+public static const int BUTTON1 = DWT.BUTTON1;
+/** @see DWT#BUTTON2 */
+public static const int BUTTON2 = DWT.BUTTON2;
+/** @see DWT#BUTTON3 */
+public static const int BUTTON3 = DWT.BUTTON3;
+/** @see DWT#BUTTON4 */
+public static const int BUTTON4 = DWT.BUTTON4;
+/** @see DWT#BUTTON5 */
+public static const int BUTTON5 = DWT.BUTTON5;
+/** A bitwise OR'ing of {@link #BUTTON1}, {@link #BUTTON2}, {@link #BUTTON3},
+ * {@link #BUTTON4} and {@link #BUTTON5} */
+public static const int ANY_BUTTON = DWT.BUTTON_MASK;
+
+/**
+ * Constructs a new InputEvent.
+ * @param dispatcher the event dispatcher
+ * @param source the source of the event
+ * @param state the state
+ */
+public this(EventDispatcher dispatcher, IFigure source, int state) {
+    super(cast(Object)source);
+    this.state = state;
+}
+
+/**
+ * Marks this event as consumed so that it doesn't get passed on to other listeners.
+ */
+public void consume() {
+    consumed = true;
+}
+
+/**
+ * Returns the event statemask, which is a bitwise OR'ing of any of the following:
+ * {@link #ALT}, {@link #CONTROL}, {@link #SHIFT}, {@link #BUTTON1}, {@link #BUTTON2},
+ * {@link #BUTTON3}, {@link #BUTTON4} and {@link #BUTTON5}.
+ * @return the state
+ */
+public int getState() {
+    return state;
+}
+
+/**
+ * @return whether this event has been consumed.
+ */
+public bool isConsumed() {
+    return consumed;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/KeyEvent.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.KeyEvent;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.InputEvent;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.EventDispatcher;
+static import dwt.events.KeyEvent;
+
+/**
+ * An event caused by the user interacting with the keyboard.
+ */
+public class KeyEvent
+    : InputEvent
+{
+
+/**
+ * The character that was pressed.
+ * @see dwt.events.KeyEvent#character
+ */
+public char character;
+
+/**
+ * The keycode.
+ * @see dwt.events.KeyEvent#keyCode
+ */
+public int keycode;
+
+/**
+ * Constructs a new KeyEvent.
+ * @param dispatcher the event dispatcher
+ * @param source the source of the event
+ * @param ke an DWT key event used to supply the statemask, keycode and character
+ */
+public this(EventDispatcher dispatcher, IFigure source,
+                    dwt.events.KeyEvent.KeyEvent ke) {
+    super(dispatcher, source, ke.stateMask);
+    character = ke.character;
+    keycode = ke.keyCode;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/KeyListener.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.KeyListener;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.KeyEvent;
+
+/**
+ * A listener interface for receiving {@link KeyEvent KeyEvents} from the keyboard.
+ */
+public interface KeyListener {
+
+/**
+ * Called when a key is pressed.
+ * @param ke The KeyEvent object
+ */
+void keyPressed(KeyEvent ke);
+
+/**
+ * Called when a key is released.
+ * @param ke The KeyEvent object
+ */
+void keyReleased(KeyEvent ke);
+
+}
+
+/**
+ * An empty implementation of KeyListener for convenience.
+ */
+class KeyListenerStub
+    : KeyListener
+{
+    /**
+     * @see dwtx.draw2d.KeyListener#keyPressed(KeyEvent)
+     */
+    public void keyPressed(KeyEvent ke) { }
+    /**
+     * @see dwtx.draw2d.KeyListener#keyReleased(KeyEvent)
+     */
+    public void keyReleased(KeyEvent ke) { }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Label.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,706 @@
+/*******************************************************************************
+ * 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.Label;
+
+import dwt.dwthelper.utils;
+
+
+
+import dwt.graphics.Font;
+import dwt.graphics.Image;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.Figure;
+import dwtx.draw2d.PositionConstants;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.TextUtilities;
+import dwtx.draw2d.ColorConstants;
+
+/**
+ * A figure that can display text and/or an image.
+ */
+public class Label
+    : Figure
+    , PositionConstants
+{
+alias Figure.getPreferredSize getPreferredSize;
+
+private static String ELLIPSIS = "..."; //$NON-NLS-1$
+
+
+private Image icon;
+private String text = "";//$NON-NLS-1$
+private String subStringText;
+private Dimension textSize;
+private Dimension subStringTextSize;
+private Dimension iconSize;
+private Point iconLocation;
+private Point textLocation;
+private int textAlignment = CENTER;
+private int iconAlignment = CENTER;
+private int labelAlignment = CENTER;
+private int textPlacement = EAST;
+private int iconTextGap = 3;
+
+/**
+ * Construct an empty Label.
+ *
+ * @since 2.0
+ */
+public this() {
+    iconSize = new Dimension(0, 0);
+}
+
+/**
+ * Construct a Label with passed String as its text.
+ *
+ * @param s the label text
+ * @since 2.0
+ */
+public this(String s) {
+    this();
+    setText(s);
+}
+
+/**
+ * Construct a Label with passed Image as its icon.
+ *
+ * @param i the label image
+ * @since 2.0
+ */
+public this(Image i) {
+    this();
+    setIcon(i);
+}
+
+/**
+ * Construct a Label with passed String as text and passed Image as its icon.
+ *
+ * @param s the label text
+ * @param i the label image
+ * @since 2.0
+ */
+public this(String s, Image i) {
+    this();
+    setText(s);
+    setIcon(i);
+}
+
+private void alignOnHeight(Point loc, Dimension size, int alignment) {
+    Insets insets = getInsets();
+    switch(alignment) {
+        case TOP:
+            loc.y = insets.top;
+            break;
+        case BOTTOM:
+            loc.y = bounds.height - size.height - insets.bottom;
+            break;
+        default:
+            loc.y = (bounds.height - size.height) / 2;
+    }
+}
+
+private void alignOnWidth(Point loc, Dimension size, int alignment) {
+    Insets insets = getInsets();
+    switch(alignment) {
+        case LEFT:
+            loc.x = insets.left;
+            break;
+        case RIGHT:
+            loc.x = bounds.width - size.width - insets.right;
+            break;
+        default:
+            loc.x = (bounds.width - size.width) / 2;
+    }
+}
+
+private void calculateAlignment() {
+    switch(textPlacement) {
+        case EAST:
+        case WEST:
+            alignOnHeight(textLocation, getTextSize(), textAlignment);
+            alignOnHeight(iconLocation, getIconSize(), iconAlignment);
+            break;
+        case NORTH:
+        case SOUTH:
+            alignOnWidth(textLocation, getSubStringTextSize(), textAlignment);
+            alignOnWidth(iconLocation, getIconSize(), iconAlignment);
+            break;
+    }
+}
+
+/**
+ * Calculates the size of the Label using the passed Dimension as the size of the Label's
+ * text.
+ *
+ * @param txtSize the precalculated size of the label's text
+ * @return the label's size
+ * @since 2.0
+ */
+protected Dimension calculateLabelSize(Dimension txtSize) {
+    int gap = getIconTextGap();
+    if (getIcon() is null || getText().equals("")) //$NON-NLS-1$
+        gap = 0;
+    Dimension d = new Dimension(0, 0);
+    if (textPlacement is WEST || textPlacement is EAST) {
+        d.width = getIconSize().width + gap + txtSize.width;
+        d.height = Math.max(getIconSize().height, txtSize.height);
+    } else {
+        d.width = Math.max(getIconSize().width, txtSize.width);
+        d.height = getIconSize().height + gap + txtSize.height;
+    }
+    return d;
+}
+
+private void calculateLocations() {
+    textLocation = new Point();
+    iconLocation = new Point();
+
+    calculatePlacement();
+    calculateAlignment();
+    Dimension offset = getSize().getDifference(getPreferredSize());
+    offset.width += getTextSize().width - getSubStringTextSize().width;
+    switch (labelAlignment) {
+        case CENTER:
+            offset.scale(0.5f);
+            break;
+        case LEFT:
+            offset.scale(0.0f);
+            break;
+        case RIGHT:
+            offset.scale(1.0f);
+            break;
+        case TOP:
+            offset.height = 0;
+            offset.scale(0.5f);
+            break;
+        case BOTTOM:
+            offset.height = offset.height * 2;
+            offset.scale(0.5f);
+            break;
+        default:
+            offset.scale(0.5f);
+            break;
+    }
+
+    switch (textPlacement) {
+        case EAST:
+        case WEST:
+            offset.height = 0;
+            break;
+        case NORTH:
+        case SOUTH:
+            offset.width = 0;
+            break;
+    }
+
+    textLocation.translate(offset);
+    iconLocation.translate(offset);
+}
+
+private void calculatePlacement() {
+    int gap = getIconTextGap();
+    if (icon is null || text.equals("")) //$NON-NLS-1$
+        gap = 0;
+    Insets insets = getInsets();
+
+    switch(textPlacement) {
+    case EAST:
+        iconLocation.x = insets.left;
+        textLocation.x = getIconSize().width + gap + insets.left;
+        break;
+    case WEST:
+        textLocation.x = insets.left;
+        iconLocation.x = getSubStringTextSize().width + gap + insets.left;
+        break;
+    case NORTH:
+        textLocation.y = insets.top;
+        iconLocation.y = getTextSize().height + gap + insets.top;
+        break;
+    case SOUTH:
+        textLocation.y = getIconSize().height + gap + insets.top;
+        iconLocation.y = insets.top;
+    }
+}
+
+/**
+ * Calculates the size of the Label's text size. The text size calculated takes into
+ * consideration if the Label's text is currently truncated. If text size without
+ * considering current truncation is desired, use {@link #calculateTextSize()}.
+ *
+ * @return the size of the label's text, taking into account truncation
+ * @since 2.0
+ */
+protected Dimension calculateSubStringTextSize() {
+    return getTextUtilities().getTextExtents(getSubStringText(), getFont());
+}
+
+/**
+ * Calculates and returns the size of the Label's text. Note that this Dimension is
+ * calculated using the Label's full text, regardless of whether or not its text is
+ * currently truncated. If text size considering current truncation is desired, use
+ * {@link #calculateSubStringTextSize()}.
+ *
+ * @return the size of the label's text, ignoring truncation
+ * @since 2.0
+ */
+protected Dimension calculateTextSize() {
+    return getTextUtilities().getTextExtents(getText(), getFont());
+}
+
+private void clearLocations() {
+    iconLocation = textLocation = null;
+}
+
+/**
+ * Returns the Label's icon.
+ *
+ * @return the label icon
+ * @since 2.0
+ */
+public Image getIcon() {
+    return icon;
+}
+
+/**
+ * Returns the current alignment of the Label's icon. The default is
+ * {@link PositionConstants#CENTER}.
+ *
+ * @return the icon alignment
+ * @since 2.0
+ */
+public int getIconAlignment() {
+    return iconAlignment;
+}
+
+/**
+ * Returns the bounds of the Label's icon.
+ *
+ * @return the icon's bounds
+ * @since 2.0
+ */
+public Rectangle getIconBounds() {
+    Rectangle bounds = getBounds();
+    return new Rectangle(bounds.getLocation().translate(getIconLocation()), getIconSize());
+}
+
+/**
+ * Returns the location of the Label's icon relative to the Label.
+ *
+ * @return the icon's location
+ * @since 2.0
+ */
+protected Point getIconLocation() {
+    if (iconLocation is null)
+        calculateLocations();
+    return iconLocation;
+}
+
+/**
+ * Returns the gap in pixels between the Label's icon and its text.
+ *
+ * @return the gap
+ * @since 2.0
+ */
+public int getIconTextGap() {
+    return iconTextGap;
+}
+
+/**
+ * @see IFigure#getMinimumSize(int, int)
+ */
+public Dimension getMinimumSize(int w, int h) {
+    if (minSize !is null)
+        return minSize;
+    minSize = new Dimension();
+    if (getLayoutManager() !is null)
+        minSize.setSize(getLayoutManager().getMinimumSize(this, w, h));
+
+    Dimension labelSize =
+        calculateLabelSize(getTextUtilities().getTextExtents(getTruncationString(), getFont())
+                .intersect(getTextUtilities().getTextExtents(getText(), getFont())));
+    Insets insets = getInsets();
+    labelSize.expand(insets.getWidth(), insets.getHeight());
+    minSize.union_(labelSize);
+    return minSize;
+}
+
+/**
+ * @see IFigure#getPreferredSize(int, int)
+ */
+public Dimension getPreferredSize(int wHint, int hHint) {
+    if (prefSize is null) {
+        prefSize = calculateLabelSize(getTextSize());
+        Insets insets = getInsets();
+        prefSize.expand(insets.getWidth(), insets.getHeight());
+        if (getLayoutManager() !is null)
+            prefSize.union_(getLayoutManager().getPreferredSize(this, wHint, hHint));
+    }
+    if (wHint >= 0 && wHint < prefSize.width) {
+        Dimension minSize = getMinimumSize(wHint, hHint);
+        Dimension result = prefSize.getCopy();
+        result.width = Math.min(result.width, wHint);
+        result.width = Math.max(minSize.width, result.width);
+        return result;
+    }
+    return prefSize;
+}
+
+/**
+ * Calculates the amount of the Label's current text will fit in the Label, including an
+ * elipsis "..." if truncation is required.
+ *
+ * @return the substring
+ * @since 2.0
+ */
+public String getSubStringText() {
+    if (subStringText !is null)
+        return subStringText;
+
+    subStringText = text;
+    int widthShrink = getPreferredSize().width - getSize().width;
+    if (widthShrink <= 0)
+        return subStringText;
+
+    Dimension effectiveSize = getTextSize().getExpanded(-widthShrink, 0);
+    Font currentFont = getFont();
+    int dotsWidth = getTextUtilities().getTextExtents(getTruncationString(), currentFont).width;
+
+    if (effectiveSize.width < dotsWidth)
+        effectiveSize.width = dotsWidth;
+
+    int subStringLength = getTextUtilities().getLargestSubstringConfinedTo(text,
+                                                  currentFont,
+                                                  effectiveSize.width - dotsWidth);
+    subStringText = (text.substring(0, subStringLength) ~ getTruncationString()).dup;
+    return subStringText;
+}
+
+/**
+ * Returns the size of the Label's current text. If the text is currently truncated, the
+ * truncated text with its ellipsis is used to calculate the size.
+ *
+ * @return the size of this label's text, taking into account truncation
+ * @since 2.0
+ */
+protected Dimension getSubStringTextSize() {
+    if (subStringTextSize is null)
+        subStringTextSize = calculateSubStringTextSize();
+    return subStringTextSize;
+}
+
+/**
+ * Returns the text of the label. Note that this is the complete text of the label,
+ * regardless of whether it is currently being truncated. Call {@link #getSubStringText()}
+ * to return the label's current text contents with truncation considered.
+ *
+ * @return the complete text of this label
+ * @since 2.0
+ */
+public String getText() {
+    return text;
+}
+
+/**
+ * Returns the current alignment of the Label's text. The default text alignment is
+ * {@link PositionConstants#CENTER}.
+ *
+ * @return the text alignment
+ */
+public int getTextAlignment() {
+    return textAlignment;
+}
+
+/**
+ * Returns the bounds of the label's text. Note that the bounds are calculated using the
+ * label's complete text regardless of whether the label's text is currently truncated.
+ *
+ * @return the bounds of this label's complete text
+ * @since 2.0
+ */
+public Rectangle getTextBounds() {
+    Rectangle bounds = getBounds();
+    return new Rectangle(bounds.getLocation().translate(getTextLocation()), textSize);
+}
+
+/**
+ * Returns the location of the label's text relative to the label.
+ *
+ * @return the text location
+ * @since 2.0
+ */
+protected Point getTextLocation() {
+    if (textLocation !is null)
+        return textLocation;
+    calculateLocations();
+    return textLocation;
+}
+
+/**
+ * Returns the current placement of the label's text relative to its icon. The default
+ * text placement is {@link PositionConstants#EAST}.
+ *
+ * @return the text placement
+ * @since 2.0
+ */
+public int getTextPlacement() {
+    return textPlacement;
+}
+
+/**
+ * Returns the size of the label's complete text. Note that the text used to make this
+ * calculation is the label's full text, regardless of whether the label's text is
+ * currently being truncated and is displaying an ellipsis. If the size considering
+ * current truncation is desired, call {@link #getSubStringTextSize()}.
+ *
+ * @return the size of this label's complete text
+ * @since 2.0
+ */
+protected Dimension getTextSize() {
+    if (textSize is null)
+        textSize = calculateTextSize();
+    return textSize;
+}
+
+/**
+ * @see IFigure#invalidate()
+ */
+public void invalidate() {
+    prefSize = null;
+    minSize = null;
+    clearLocations();
+    textSize = null;
+    subStringTextSize = null;
+    subStringText = null;
+    super.invalidate();
+}
+
+/**
+ * Returns <code>true</code> if the label's text is currently truncated and is displaying
+ * an ellipsis, <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if the label's text is truncated
+ * @since 2.0
+ */
+public bool isTextTruncated() {
+    return !getSubStringText().equals(getText());
+}
+
+/**
+ * @see Figure#paintFigure(Graphics)
+ */
+protected void paintFigure(Graphics graphics) {
+    if (isOpaque())
+        super.paintFigure(graphics);
+    Rectangle bounds = getBounds();
+    graphics.translate(bounds.x, bounds.y);
+    if (icon !is null)
+        graphics.drawImage(icon, getIconLocation());
+    if (!isEnabled()) {
+        graphics.translate(1, 1);
+        graphics.setForegroundColor(ColorConstants.buttonLightest);
+        graphics.drawText(getSubStringText(), getTextLocation());
+        graphics.translate(-1, -1);
+        graphics.setForegroundColor(ColorConstants.buttonDarker);
+    }
+    graphics.drawText(getSubStringText(), getTextLocation());
+    graphics.translate(-bounds.x, -bounds.y);
+}
+
+/**
+ * Sets the label's icon to the passed image.
+ *
+ * @param image the new label image
+ * @since 2.0
+ */
+public void setIcon(Image image) {
+    if (icon is image)
+        return;
+    icon = image;
+    //Call repaint, in case the image dimensions are the same.
+    repaint();
+    if (icon is null)
+        setIconDimension(new Dimension());
+    else
+        setIconDimension(new Dimension(image));
+}
+
+/**
+ * This method sets the alignment of the icon within the bounds of the label. If the label
+ * is larger than the icon, then the icon will be aligned according to this alignment.
+ * Valid values are:
+ * <UL>
+ *   <LI><EM>{@link PositionConstants#CENTER}</EM>
+ *   <LI>{@link PositionConstants#TOP}
+ *   <LI>{@link PositionConstants#BOTTOM}
+ *   <LI>{@link PositionConstants#LEFT}
+ *   <LI>{@link PositionConstants#RIGHT}
+ * </UL>
+ * @param align the icon alignment
+ * @since 2.0
+ */
+public void setIconAlignment (int align_) {
+    if (iconAlignment is align_)
+        return;
+    iconAlignment = align_;
+    clearLocations();
+    repaint();
+}
+
+/**
+ * Sets the label's icon size to the passed Dimension.
+ *
+ * @param d the new icon size
+ * @deprecated the icon is automatically displayed at 1:1
+ * @since 2.0
+ */
+public void setIconDimension(Dimension d) {
+    if (d.opEquals(getIconSize()))
+        return;
+    iconSize = d;
+    revalidate();
+}
+
+/**
+ * Sets the gap in pixels between the label's icon and text to the passed value. The
+ * default is 4.
+ *
+ * @param gap the gap
+ * @since 2.0
+ */
+public void setIconTextGap(int gap) {
+    if (iconTextGap is gap)
+        return;
+    iconTextGap = gap;
+    repaint();
+    revalidate();
+}
+
+/**
+ * Sets the alignment of the label (icon and text) within the figure. If this
+ * figure's bounds are larger than the size needed to display the label, the
+ * label will be aligned accordingly. Valid values are:
+ * <UL>
+ *   <LI><EM>{@link PositionConstants#CENTER}</EM>
+ *   <LI>{@link PositionConstants#TOP}
+ *   <LI>{@link PositionConstants#BOTTOM}
+ *   <LI>{@link PositionConstants#LEFT}
+ *   <LI>{@link PositionConstants#RIGHT}
+ * </UL>
+ *
+ * @param align label alignment
+ */
+public void setLabelAlignment(int align_) {
+    if (labelAlignment is align_)
+        return;
+    labelAlignment = align_;
+    clearLocations();
+    repaint();
+}
+
+/**
+ * Sets the label's text.
+ * @param s the new label text
+ * @since 2.0
+ */
+public void setText(String s) {
+    //"text" will never be null.
+    if (s is null)
+        s = "";//$NON-NLS-1$
+    if (text.equals(s))
+        return;
+    text = s;
+    revalidate();
+    repaint();
+}
+
+/**
+ * Sets the alignment of the text relative to the icon within the label. The text
+ * alignment must be orthogonal to the text placement. For example, if the placement
+ * is EAST, then the text can be aligned using TOP, CENTER, or BOTTOM. Valid values are:
+ * <UL>
+ *   <LI><EM>{@link PositionConstants#CENTER}</EM>
+ *   <LI>{@link PositionConstants#TOP}
+ *   <LI>{@link PositionConstants#BOTTOM}
+ *   <LI>{@link PositionConstants#LEFT}
+ *   <LI>{@link PositionConstants#RIGHT}
+ * </UL>
+ * @see #setLabelAlignment(int)
+ * @param align the text alignment
+ * @since 2.0
+ */
+public void setTextAlignment(int align_) {
+    if (textAlignment is align_)
+        return;
+    textAlignment = align_;
+    clearLocations();
+    repaint();
+}
+
+/**
+ * Sets the placement of the text relative to the icon within the label.
+ * Valid values are:
+ * <UL>
+ *   <LI><EM>{@link PositionConstants#EAST}</EM>
+ *   <LI>{@link PositionConstants#NORTH}
+ *   <LI>{@link PositionConstants#SOUTH}
+ *   <LI>{@link PositionConstants#WEST}
+ * </UL>
+ *
+ * @param where the text placement
+ * @since 2.0
+ */
+public void setTextPlacement (int where) {
+    if (textPlacement is where)
+        return;
+    textPlacement = where;
+    revalidate();
+    repaint();
+}
+
+/**
+ * Gets the <code>TextUtilities</code> instance to be used in measurement
+ * calculations.
+ *
+ * @return a <code>TextUtilities</code> instance
+ * @since 3.4
+ */
+public TextUtilities getTextUtilities() {
+    return TextUtilities.INSTANCE;
+}
+
+/**
+ * Gets the string that will be appended to the text when the label is
+ * truncated. By default, this returns an ellipsis.
+ *
+ * @return the string to append to the text when truncated
+ * @since 3.4
+ */
+protected String getTruncationString() {
+    return ELLIPSIS;
+}
+
+/**
+ * Gets the icon size
+ *
+ * @return the icon size
+ * @since 3.4
+ */
+protected Dimension getIconSize() {
+    return iconSize;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/LabelAnchor.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.LabelAnchor;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.ChopboxAnchor;
+import dwtx.draw2d.Label;
+
+/**
+ * LabelAnchors must have an owner of type {@link Label}. The LabelAnchor behaves like
+ * {@link ChopboxAnchor} but {@link Connection Connections} will point to the center of
+ * its owner's icon as opposed to the center of the entire owning Label.
+ */
+public class LabelAnchor
+    : ChopboxAnchor
+{
+
+/**
+ * Constructs a LabelAnchor with no owner.
+ *
+ * @since 2.0
+ */
+protected this() { }
+
+/**
+ * Constructs a LabelAnchor with owner <i>label</i>.
+ * @param label This LabelAnchor's owner
+ * @since 2.0
+ */
+public this(Label label) {
+    super(label);
+}
+
+/**
+ * Returns the bounds of this LabelAnchor's owning Label icon.
+ * @return The bounds of this LabelAnchor's owning Label icon
+ * @since 2.0
+ */
+protected Rectangle getBox() {
+    Label label = cast(Label)getOwner();
+    return label.getIconBounds();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/LabeledBorder.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.LabeledBorder;
+
+import dwt.dwthelper.utils;
+
+import dwt.graphics.Font;
+import dwtx.draw2d.Border;
+
+/**
+ * LabeledBorders have a text message somewhere on them. The Font for the text can be set.
+ * LabeledBorders should not change their Insets when the label changes, therefore,
+ * Figures using this Border should repaint() when updating the label, and revalidate()
+ * when changing the Font.
+ */
+public interface LabeledBorder
+    : Border
+{
+
+/**
+ * Returns the label for this Border.
+ * @return The label for this Border
+ */
+String getLabel();
+
+/**
+ * Sets the Font for the label.
+ * @param f The Font to be set
+ */
+void setFont(Font f);
+
+/**
+ * Sets the text to be displayed as the label for this Border.
+ * @param l The text
+ */
+void setLabel(String l);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/LabeledContainer.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.LabeledContainer;
+
+import dwt.dwthelper.utils;
+
+import dwt.graphics.Font;
+import dwtx.draw2d.Figure;
+import dwtx.draw2d.Border;
+import dwtx.draw2d.LabeledBorder;
+import dwtx.draw2d.GroupBoxBorder;
+import dwtx.draw2d.CompoundBorder;
+
+/**
+ * A Container with a title bar describing the contents of the container. The frame is
+ * generated by a {@link LabeledBorder}.
+ */
+public class LabeledContainer
+    : Figure
+{
+
+/**
+ * Constructs a default container with a {@link GroupBoxBorder}.
+ *
+ * @since 2.0
+ */
+public this() {
+    this(new GroupBoxBorder());
+}
+
+/**
+ * Constructs a labeled container with the border given as input.
+ *
+ * @param border the border
+ * @since 2.0
+ */
+public this(Border border) {
+    setBorder(border);
+    setOpaque(true);
+}
+
+private static LabeledBorder findLabeledBorder(Border border) {
+    if ( auto b = cast(LabeledBorder)border )
+        return b;
+    if ( auto cb = cast(CompoundBorder)border ) {
+        LabeledBorder labeled = findLabeledBorder(cb.getInnerBorder());
+        if (labeled is null)
+            labeled = findLabeledBorder(cb.getOuterBorder());
+        return labeled;
+    }
+    return null;
+}
+
+/**
+ * Returns the text of the LabeledContainer's label.
+ *
+ * @return the label text
+ * @since 2.0
+ */
+public String getLabel() {
+    return getLabeledBorder().getLabel();
+}
+
+/**
+ * Returns the LabeledBorder of this container.
+ *
+ * @return the border
+ * @since 2.0
+ */
+protected LabeledBorder getLabeledBorder() {
+    return findLabeledBorder(getBorder());
+}
+
+/**
+ * Sets the title of the container.
+ *
+ * @param s the new title text
+ * @since 2.0
+ */
+public void setLabel(String s) {
+    getLabeledBorder().setLabel(s);
+    revalidate();
+    repaint();
+}
+
+/**
+ * Sets the font to be used for the container title.
+ *
+ * @param f the new font
+ * @since 2.0
+ */
+public void setLabelFont(Font f) {
+    getLabeledBorder().setFont(f);
+    revalidate();
+    repaint();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Layer.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.Layer;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.Figure;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.TreeSearch;
+
+
+/**
+ * A transparent figure intended to be added exclusively to a {@link LayeredPane}, who has
+ * the responsibilty of managing its layers.
+ */
+public class Layer
+    : Figure
+{
+
+/**
+ * Overridden to implement transparent behavior.
+ * @see IFigure#containsPoint(int, int)
+ *
+ */
+public bool containsPoint(int x, int y) {
+    if (isOpaque())
+        return super.containsPoint(x, y);
+    Point pt = new Point(x, y);
+    translateFromParent(pt);
+    for (int i = 0; i < getChildren().size(); i++) {
+        IFigure child = cast(IFigure)getChildren().get(i);
+        if (child.containsPoint(pt.x, pt.y))
+            return true;
+    }
+    return false;
+}
+
+/**
+ * Overridden to implement transparency.
+ * @see IFigure#findFigureAt(int, int, TreeSearch)
+ */
+public IFigure findFigureAt(int x, int y, TreeSearch search) {
+    if (!isEnabled())
+        return null;
+    if (isOpaque())
+        return super.findFigureAt(x, y, search);
+
+    IFigure f = super.findFigureAt(x, y, search);
+    if (f is this)
+        return null;
+    return f;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/LayeredPane.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,152 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.LayeredPane;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.draw2d.Layer;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.StackLayout;
+
+/**
+ * A figure capable of holding any number of layers. Only layers can be added to this
+ * figure. Layers are added to this figure with thier respective keys, which are used to
+ * identify them.
+ */
+public class LayeredPane
+    : Layer
+{
+
+private List layerKeys;
+
+/**
+ * Constructs a new layered pane with no layers in it.
+ */
+public this() {
+    layerKeys = new ArrayList();
+    setLayoutManager(new StackLayout());
+}
+
+/**
+ * Adds the given layer figure, identifiable with the given key, at the specified index.
+ * While adding the layer, it informs the surrounding layers of the addition.
+ *
+ * @param figure the layer
+ * @param layerKey the layer's key
+ * @param index the index where the layer should be added
+ * @since 2.0
+ */
+public void add(IFigure figure, Object layerKey, int index) {
+    if (index is -1)
+        index = layerKeys.size();
+    super.add(figure, null, index);
+    layerKeys.add(index, layerKey);
+}
+
+/**
+ * Adds the given layer, identifiable with the given key, under the <i>after</i> layer
+ * provided in the input.
+ *
+ * @param layer the layer
+ * @param key the layer's key
+ * @param after the layer under which the input layer should be added
+ * @since 2.0
+ */
+public void addLayerAfter(Layer layer, Object key, Object after) {
+    int index = layerKeys.indexOf(after);
+    add(layer, key, ++index);
+}
+
+/**
+ * Adds the given layer, identifiable with the given key, above the <i>before</i> layer
+ * provided in the input.
+ *
+ * @param layer the layer
+ * @param key the layer's key
+ * @param before the layer above which the input layer should be added
+ * @since 2.0
+ */
+public void addLayerBefore(Layer layer, Object key, Object before) {
+    int index = layerKeys.indexOf(before);
+    add(layer, key, index);
+}
+
+/**
+ * Returns the layer identified by the key given in the input.
+ *
+ * @param key the key to identify the desired layer
+ * @return the desired layer
+ * @since 2.0
+ */
+public Layer getLayer(Object key) {
+    int index = layerKeys.indexOf(key);
+    if (index is -1)
+        return null;
+    return cast(Layer)getChildren().get(index);
+}
+
+/**
+ * Returns the layer at the specified index in this pane.
+ *
+ * @param index the index of the desired layer
+ * @return the desired layer
+ * @since 2.0
+ */
+protected Layer getLayer(int index) {
+    return cast(Layer)getChildren().get(index);
+}
+
+/**
+ * @see dwtx.draw2d.IFigure#remove(dwtx.draw2d.IFigure)
+ */
+public void remove(IFigure figure) {
+    int index = getChildren().indexOf(cast(Object)figure);
+    if (index !is -1)
+        layerKeys.remove(index);
+    super.remove(figure);
+}
+
+/**
+ * Removes the layer identified by the given key from this layered pane.
+ *
+ * @param key the key of the layer to be removed
+ * @since 2.0
+ */
+public void removeLayer(Object key) {
+    removeLayer(layerKeys.indexOf(key));
+}
+
+/**
+ * Removes the given layer from this layered pane.
+ *
+ * @deprecated call {@link IFigure#remove(IFigure)} instead
+ * @param layer the layer to be removed
+ * @since 2.0
+ */
+public void removeLayer(IFigure layer) {
+    remove(layer);
+}
+
+/**
+ * Removes the layer at the specified index from the list of layers in this layered pane.
+ * It collapses the layers, occupying the space vacated by the removed layer.
+ *
+ * @param index the index of the layer to be removed
+ * @since 2.0
+ */
+protected void removeLayer(int index) {
+    Layer removeLayer = getLayer(index);
+    remove(removeLayer);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/LayoutAnimator.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.LayoutAnimator;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.Animator;
+import dwtx.draw2d.LayoutListener;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Animation;
+
+/**
+ * Animates the layout of a figure's children. The animator will capture the effects of a
+ * layout manager, and then play back the placement of children using linear interpolation
+ * for each child's start and end locations.
+ * <P>
+ * To use an animator, hook it as a layout listener for the figure whose layout is to
+ * be animated, by calling {@link IFigure#addLayoutListener(LayoutListener)}. It is not
+ * necessary to have an animator for every figure in a composition that is undergoing
+ * animation. For example, if a figure without an animator moves during the animation, it
+ * will continue to move and layout its children normally during each step of the
+ * animation.
+ * <P>
+ * Animator must be used in conjunction with layouts. If figures are placed manually using
+ * <code>setBounds()</code>, the animator may not be able to track and playback the
+ * changes that occur.
+ *
+ * @since 3.2
+ */
+public class LayoutAnimator : Animator , LayoutListener {
+
+static const LayoutAnimator INSTANCE;
+
+static this(){
+    INSTANCE = new LayoutAnimator();
+}
+/**
+ * Constructs a new Animator. The default instance ({@link #getDefault()}) can be used on
+ * all figures being animated.
+ *
+ * @since 3.2
+ */
+protected this() { }
+
+/**
+ * Returns an object encapsulating the placement of children in a container. This method
+ * is called to capture both the initial and final states.
+ * @param container the container figure
+ * @return the current state
+ * @since 3.2
+ */
+protected Object getCurrentState(IFigure container) {
+    Map locations = new HashMap();
+    List children = container.getChildren();
+    IFigure child;
+    for (int i = 0; i < children.size(); i++) {
+        child = cast(IFigure)children.get(i);
+        locations.put(cast(Object)child, child.getBounds().getCopy());
+    }
+    return cast(Object)locations;
+}
+
+/**
+ * Returns the default instance.
+ * @return the default instance
+ * @since 3.2
+ */
+public static LayoutAnimator getDefault() {
+    return INSTANCE;
+}
+
+/**
+ * Hooks invalidation in case animation is in progress.
+ * @see LayoutListener#invalidate(IFigure)
+ */
+public final void invalidate(IFigure container) {
+    if (Animation.isInitialRecording())
+        Animation.hookAnimator(container, this);
+}
+
+/**
+ * Hooks layout in case animation is in progress.
+ * @see dwtx.draw2d.LayoutListener#layout(dwtx.draw2d.IFigure)
+ */
+public final bool layout(IFigure container) {
+    if (Animation.isAnimating())
+        return Animation.hookPlayback(container, this);
+    return false;
+}
+
+/**
+ * Plays back the animated layout.
+ * @see Animator#playback(IFigure)
+ */
+protected bool playback(IFigure container) {
+    Map initial = cast(Map) Animation.getInitialState(this, container);
+    Map ending = cast(Map) Animation.getFinalState(this, container);
+    if (initial is null)
+        return false;
+    List children = container.getChildren();
+
+    float progress = Animation.getProgress();
+    float ssergorp = 1 - progress;
+
+    Rectangle rect1, rect2;
+
+    for (int i = 0; i < children.size(); i++) {
+        IFigure child = cast(IFigure) children.get(i);
+        rect1 = cast(Rectangle)initial.get(cast(Object)child);
+        rect2 = cast(Rectangle)ending.get(cast(Object)child);
+
+        //TODO need to change this to hide the figure until the end.
+        if (rect1 is null)
+            continue;
+        child.setBounds(new Rectangle(
+            cast(int)Math.round(progress * rect2.x + ssergorp * rect1.x),
+            cast(int)Math.round(progress * rect2.y + ssergorp * rect1.y),
+            cast(int)Math.round(progress * rect2.width + ssergorp * rect1.width),
+            cast(int)Math.round(progress * rect2.height + ssergorp * rect1.height)
+        ));
+    }
+    return true;
+}
+
+/**
+ * Hooks post layout in case animation is in progress.
+ * @see LayoutListener#postLayout(IFigure)
+ */
+public final void postLayout(IFigure container) {
+    if (Animation.isFinalRecording())
+        Animation.hookNeedsCapture(container, this);
+}
+
+/**
+ * This callback is unused. Reserved for possible future use.
+ * @see LayoutListener#remove(IFigure)
+ */
+public final void remove(IFigure child) { }
+
+/**
+ * This callback is unused. Reserved for possible future use.
+ * @see LayoutListener#setConstraint(IFigure, Object)
+ */
+public final void setConstraint(IFigure child, Object constraint) { }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/LayoutListener.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.LayoutListener;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.IFigure;
+
+
+/**
+ * Classes which implement this interface provide callback hooks for various layout
+ * related events.
+ * <P>
+ * Instances can be hooked to figures by calling {@link
+ * IFigure#addLayoutListener(LayoutListener)}. Listeners will be made aware of various
+ * steps of the layout mechanism, and even have the opportunity to prevent normal layout
+ * from occurring.
+ * @since 3.1
+ */
+public interface LayoutListener {
+
+/**
+ * Called when a container has been invalidated.
+ * @param container the invalidated Figure
+ * @since 3.1
+ */
+void invalidate(IFigure container);
+
+/**
+ * Called prior to layout occurring.  A listener may intercept a layout by
+ * returning <code>true</code>.  If the layout is intercepted, the container's
+ * <code>LayoutManager</code> will not receive a layout call.
+ * @param container the figure incurring a layout
+ * @return <code>true</code> if the layout has been intercepted by the listener
+ * @since 3.1
+ */
+bool layout(IFigure container);
+
+/**
+ * Called after layout has occurred.
+ * @since 3.1
+ * @param container the figure incurring a layout
+ */
+void postLayout(IFigure container);
+
+/**
+ * Called when a child is about to be removed from its parent.
+ * @since 3.1
+ * @param child the child being removed
+ */
+void remove(IFigure child);
+
+/**
+ * Called when a child's constraint is initialized or updated.
+ * @param child the child being updated
+ * @param constraint the child's new constraint
+ * @since 3.1
+ */
+void setConstraint(IFigure child, Object constraint);
+
+}
+
+/**
+ * A stub implementation which implements all of the declared methods.
+ * @since 3.1
+ */
+class LayoutListenerStub : LayoutListener {
+
+    /**
+     * Stub which does nothing.
+     * @see LayoutListener#invalidate(IFigure)
+     */
+    public void invalidate(IFigure container) { }
+
+    /**
+     * Stub which does nothing.
+     * @see LayoutListener#layout(IFigure)
+     */
+    public bool layout(IFigure container) {
+        return false;
+    }
+
+    /**
+     * Stub which does nothing.
+     * @see LayoutListener#postLayout(IFigure)
+     */
+    public void postLayout(IFigure container) { }
+
+    /**
+     * Stub which does nothing.
+     * @see LayoutListener#remove(IFigure)
+     */
+    public void remove(IFigure child) { }
+
+    /**
+     * Stub which does nothing.
+     * @see LayoutListener#setConstraint(IFigure, java.lang.Object)
+     */
+    public void setConstraint(IFigure child, Object constraint) { }
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/LayoutManager.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.LayoutManager;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.IFigure;
+
+/**
+ * A helper for positioning child figures and determining the ideal size for a figure
+ * with children.
+ */
+public interface LayoutManager {
+
+/**
+ * Returns the constraint for the given figure.
+ * @param child The figure
+ * @return The constraint
+ */
+Object getConstraint(IFigure child);
+
+/**
+ * Returns the minimum size of the given figure.
+ * @param container The Figure
+ * @param wHint the width hint
+ * @param hHint the height hint
+ * @return The minimum size
+ */
+Dimension getMinimumSize(IFigure container, int wHint, int hHint);
+
+/**
+ * Returns the preferred size of the given figure, using width and height hints.
+ * @param container The figure
+ * @param wHint The width hint
+ * @param hHint The height hint
+ * @return The preferred size
+ */
+Dimension getPreferredSize(IFigure container, int wHint, int hHint);
+
+/**
+ * Tells the LayoutManager to throw away all cached information about the figures it is
+ * responsible for. This method is called whenever the owning figure is invalidated.
+ */
+void invalidate();
+
+/**
+ * Lays out the given figure.
+ * @param container The figure
+ */
+void layout(IFigure container);
+
+/**
+ * Removes the given child from this layout.
+ * @param child the child being remoced
+ */
+void remove(IFigure child);
+
+/**
+ * Sets the constraint for the given child.
+ * @param child The figure
+ * @param constraint The constraint
+ */
+void setConstraint(IFigure child, Object constraint);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/LightweightSystem.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,560 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.LightweightSystem;
+
+import dwt.dwthelper.utils;
+
+import dwt.DWT;
+import dwt.accessibility.AccessibleControlEvent;
+import dwt.accessibility.AccessibleControlListener;
+import dwt.accessibility.AccessibleEvent;
+import dwt.accessibility.AccessibleListener;
+import dwt.events.ControlAdapter;
+import dwt.events.ControlEvent;
+import dwt.events.DisposeEvent;
+import dwt.events.DisposeListener;
+import dwt.events.FocusEvent;
+import dwt.events.FocusListener;
+import dwt.events.KeyEvent;
+import dwt.events.KeyListener;
+import dwt.events.MouseEvent;
+import dwt.events.MouseListener;
+import dwt.events.MouseMoveListener;
+import dwt.events.MouseTrackListener;
+import dwt.events.TraverseEvent;
+import dwt.events.TraverseListener;
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.GC;
+import dwt.widgets.Canvas;
+import dwt.widgets.Event;
+import dwt.widgets.Listener;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.EventDispatcher;
+import dwtx.draw2d.UpdateManager;
+import dwtx.draw2d.Figure;
+import dwtx.draw2d.DeferredUpdateManager;
+import dwtx.draw2d.SWTEventDispatcher;
+import dwtx.draw2d.StackLayout;
+import dwtx.draw2d.BufferedGraphicsSource;
+import dwtx.draw2d.NativeGraphicsSource;
+
+/**
+ * The LightweightSystem is the link between DWT and Draw2d. It is the component that
+ * provides the ability for {@link Figure Figures} to be hosted on an DWT Canvas.
+ * <p>
+ * Normal procedure for using a LightweightSystem:
+ * <ol>
+ *      <li>Create an DWT Canvas.
+ *      <li>Create a LightweightSystem passing it that Canvas.
+ *      <li>Create a Draw2d Figure and call setContents(IFigure). This Figure will be the
+ *          top-level Figure of the Draw2d application.
+ * </ol>
+ */
+public class LightweightSystem {
+
+private Canvas canvas;
+IFigure contents;
+private IFigure root;
+private EventDispatcher dispatcher;
+private UpdateManager manager;
+private int ignoreResize;
+
+/**
+ * Constructs a LightweightSystem on Canvas <i>c</i>.
+ *
+ * @param c the canvas
+ * @since 2.0
+ */
+public this(Canvas c) {
+    this();
+    setControl(c);
+}
+
+/**
+ * Constructs a LightweightSystem <b>without</b> a Canvas.
+ */
+public this() {
+    manager = new DeferredUpdateManager();
+    init();
+}
+
+/**
+ * Adds DWT listeners to the LightWeightSystem's Canvas. This allows for DWT events to be
+ * dispatched and handled by its {@link EventDispatcher}.
+ * <P>
+ * <EM>WARNING:</EM> This method should not be overridden.
+ * @since 2.0
+ */
+protected void addListeners() {
+    EventHandler handler = createEventHandler();
+    canvas.getAccessible().addAccessibleListener(handler);
+    canvas.getAccessible().addAccessibleControlListener(handler);
+    canvas.addMouseListener(handler);
+    canvas.addMouseMoveListener(handler);
+    canvas.addMouseTrackListener(handler);
+    canvas.addKeyListener(handler);
+    canvas.addTraverseListener(handler);
+    canvas.addFocusListener(handler);
+    canvas.addDisposeListener(handler);
+    canvas.addListener(DWT.MouseWheel, handler);
+
+    canvas.addControlListener(new class() ControlAdapter {
+        public void controlResized(ControlEvent e) {
+            this.outer.controlResized();
+        }
+    });
+    canvas.addListener(DWT.Paint, new class() Listener {
+        public void handleEvent(Event e) {
+            this.outer.paint(e.gc);
+        }
+    });
+}
+
+/**
+ * Resizes and revalidates the root figure when the control is resized.
+ */
+protected void controlResized() {
+    if (ignoreResize > 0)
+        return;
+    Rectangle r = new Rectangle(canvas.getClientArea());
+    r.setLocation(0, 0);
+    root.setBounds(r);
+    root.revalidate();
+    getUpdateManager().performUpdate();
+}
+
+/**
+ * Returns this LightwightSystem's EventDispatcher.
+ *
+ * @return the event dispatcher
+ * @since 2.0
+ */
+protected EventDispatcher getEventDispatcher() {
+    if (dispatcher is null)
+        setEventDispatcher(new SWTEventDispatcher());
+    return dispatcher;
+}
+
+/**
+ * Returns this LightweightSystem's root figure.
+ *
+ * @return the root figure
+ * @since 2.0
+ */
+public IFigure getRootFigure() {
+    return root;
+}
+
+/**
+ * Returns a new instance of this LightweightSystem's EventHandler.
+ *
+ * @return the newly created event handler
+ * @since 2.0
+ */
+protected final EventHandler createEventHandler() {
+    return internalCreateEventHandler();
+}
+
+/**
+ * Creates and returns the root figure.
+ *
+ * @return the newly created root figure
+ */
+protected RootFigure createRootFigure() {
+    RootFigure f = new RootFigure();
+    f.addNotify();
+    f.setOpaque(true);
+    f.setLayoutManager(new StackLayout());
+    return f;
+}
+
+/**
+ * Returns this LightweightSystem's UpdateManager.
+ *
+ * @return the update manager
+ * @since 2.0
+ */
+public UpdateManager getUpdateManager() {
+    return manager;
+}
+
+/**
+ * Initializes this LightweightSystem by setting the root figure.
+ */
+protected void init() {
+    setRootPaneFigure(createRootFigure());
+}
+
+EventHandler internalCreateEventHandler() {
+    return new EventHandler();
+}
+
+/**
+ * Invokes this LightweightSystem's {@link UpdateManager} to paint this
+ * LightweightSystem's Canvas and contents.
+ *
+ * @param gc the GC used for painting
+ * @since 2.0
+ */
+public void paint(GC gc) {
+    getUpdateManager().paint(gc);
+}
+
+/**
+ * Sets the contents of the LightweightSystem to the passed figure. This figure should be
+ * the top-level Figure in a Draw2d application.
+ *
+ * @param figure the new root figure
+ * @since 2.0
+ */
+public void setContents(IFigure figure) {
+    if (contents !is null)
+        root.remove(contents);
+    contents = figure;
+    root.add(contents);
+}
+
+/**
+ * Sets the LightweightSystem's control to the passed Canvas.
+ *
+ * @param c the canvas
+ * @since 2.0
+ */
+public void setControl(Canvas c) {
+    if (canvas is c)
+        return;
+    canvas = c;
+    if ((c.getStyle() & DWT.DOUBLE_BUFFERED) !is 0)
+        getUpdateManager().setGraphicsSource(new NativeGraphicsSource(canvas));
+    else
+        getUpdateManager().setGraphicsSource(new BufferedGraphicsSource(canvas));
+    getEventDispatcher().setControl(c);
+    addListeners();
+
+    //Size the root figure and contents to the current control's size
+    Rectangle r = new Rectangle(canvas.getClientArea());
+    r.setLocation(0, 0);
+    root.setBounds(r);
+    root.revalidate();
+}
+
+/**
+ * Sets this LightweightSystem's EventDispatcher.
+ *
+ * @param dispatcher the new event dispatcher
+ * @since 2.0
+ */
+public void setEventDispatcher(EventDispatcher dispatcher) {
+    this.dispatcher = dispatcher;
+    dispatcher.setRoot(root);
+    dispatcher.setControl(canvas);
+}
+
+void setIgnoreResize(bool value) {
+    if (value)
+        ignoreResize++;
+    else
+        ignoreResize--;
+}
+
+/**
+ * Sets this LightweightSystem's root figure.
+ * @param root the new root figure
+ */
+protected void setRootPaneFigure(RootFigure root) {
+    getUpdateManager().setRoot(root);
+    this.root = root;
+}
+
+/**
+ * Sets this LightweightSystem's UpdateManager.
+ *
+ * @param um the new update manager
+ * @since 2.0
+ */
+public void setUpdateManager(UpdateManager um) {
+    manager = um;
+    manager.setRoot(root);
+}
+
+/**
+ * The figure at the root of the LightweightSystem.  If certain properties (i.e. font,
+ * background/foreground color) are not set, the RootFigure will obtain these properties
+ * from LightweightSystem's Canvas.
+ */
+protected class RootFigure
+    : Figure
+{
+    /** @see IFigure#getBackgroundColor() */
+    public Color getBackgroundColor() {
+        if (bgColor !is null)
+            return bgColor;
+        if (canvas !is null)
+            return canvas.getBackground();
+        return null;
+    }
+
+    /** @see IFigure#getFont() */
+    public Font getFont() {
+        if (font !is null)
+            return font;
+        if (canvas !is null)
+            return canvas.getFont();
+        return null;
+    }
+
+    /** @see IFigure#getForegroundColor() */
+    public Color getForegroundColor() {
+        if (fgColor !is null)
+            return fgColor;
+        if (canvas !is null)
+            return canvas.getForeground();
+        return null;
+    }
+
+    /** @see IFigure#getUpdateManager() */
+    public UpdateManager getUpdateManager() {
+        return this.outer.getUpdateManager();
+    }
+
+    /** @see IFigure#internalGetEventDispatcher() */
+    public EventDispatcher internalGetEventDispatcher() {
+        return getEventDispatcher();
+    }
+
+    /**
+     * @see IFigure#isMirrored()
+     */
+    public bool isMirrored() {
+        return (this.outer.canvas.getStyle() & DWT.MIRRORED) !is 0;
+    }
+
+    /** @see Figure#isShowing() */
+    public bool isShowing() {
+        return true;
+    }
+}
+
+/**
+ * Listener used to get all necessary events from the Canvas and pass them on to the
+ * {@link EventDispatcher}.
+ */
+protected class EventHandler
+    : MouseMoveListener, MouseListener, AccessibleControlListener, KeyListener,
+                TraverseListener, FocusListener, AccessibleListener, MouseTrackListener,
+                Listener, DisposeListener
+{
+    /** @see FocusListener#focusGained(FocusEvent) */
+    public void focusGained(FocusEvent e) {
+        getEventDispatcher().dispatchFocusGained(e);
+    }
+
+    /** @see FocusListener#focusLost(FocusEvent) */
+    public void focusLost(FocusEvent e) {
+        getEventDispatcher().dispatchFocusLost(e);
+    }
+
+    /** @see AccessibleControlListener#getChild(AccessibleControlEvent) */
+    public void getChild(AccessibleControlEvent e) {
+        EventDispatcher.AccessibilityDispatcher ad;
+        ad = getEventDispatcher().getAccessibilityDispatcher_package();
+        if (ad !is null)
+            ad.getChild(e);
+    }
+
+    /** @see AccessibleControlListener#getChildAtPoint(AccessibleControlEvent) */
+    public void getChildAtPoint(AccessibleControlEvent e) {
+        EventDispatcher.AccessibilityDispatcher ad;
+        ad = getEventDispatcher().getAccessibilityDispatcher_package();
+        if (ad !is null)
+            ad.getChildAtPoint(e);
+    }
+
+    /** @see AccessibleControlListener#getChildCount(AccessibleControlEvent) */
+    public void getChildCount(AccessibleControlEvent e) {
+        EventDispatcher.AccessibilityDispatcher ad;
+        ad = getEventDispatcher().getAccessibilityDispatcher_package();
+        if (ad !is null)
+            ad.getChildCount(e);
+    }
+
+    /** @see AccessibleControlListener#getChildren(AccessibleControlEvent) */
+    public void getChildren(AccessibleControlEvent e) {
+        EventDispatcher.AccessibilityDispatcher ad;
+        ad = getEventDispatcher().getAccessibilityDispatcher_package();
+        if (ad !is null)
+            ad.getChildren(e);
+    }
+
+    /** @see AccessibleControlListener#getDefaultAction(AccessibleControlEvent) */
+    public void getDefaultAction(AccessibleControlEvent e) {
+        EventDispatcher.AccessibilityDispatcher ad;
+        ad = getEventDispatcher().getAccessibilityDispatcher_package();
+        if (ad !is null)
+            ad.getDefaultAction(e);
+    }
+
+    /** @see AccessibleListener#getDescription(AccessibleEvent) */
+    public void getDescription(AccessibleEvent e) {
+        EventDispatcher.AccessibilityDispatcher ad;
+        ad = getEventDispatcher().getAccessibilityDispatcher_package();
+        if (ad !is null)
+            ad.getDescription(e);
+    }
+
+    /** @see AccessibleControlListener#getFocus(AccessibleControlEvent) */
+    public void getFocus(AccessibleControlEvent e) {
+        EventDispatcher.AccessibilityDispatcher ad;
+        ad = getEventDispatcher().getAccessibilityDispatcher_package();
+        if (ad !is null)
+            ad.getFocus(e);
+    }
+
+    /** @see AccessibleListener#getHelp(AccessibleEvent) */
+    public void getHelp(AccessibleEvent e) {
+        EventDispatcher.AccessibilityDispatcher ad;
+        ad = getEventDispatcher().getAccessibilityDispatcher_package();
+        if (ad !is null)
+            ad.getHelp(e);
+    }
+
+    /** @see AccessibleListener#getKeyboardShortcut(AccessibleEvent) */
+    public void getKeyboardShortcut(AccessibleEvent e) {
+        EventDispatcher.AccessibilityDispatcher ad;
+        ad = getEventDispatcher().getAccessibilityDispatcher_package();
+        if (ad !is null)
+            ad.getKeyboardShortcut(e);
+    }
+
+    /** @see AccessibleControlListener#getLocation(AccessibleControlEvent) */
+    public void getLocation(AccessibleControlEvent e) {
+        EventDispatcher.AccessibilityDispatcher ad;
+        ad = getEventDispatcher().getAccessibilityDispatcher_package();
+        if (ad !is null)
+            ad.getLocation(e);
+    }
+
+    /** @see AccessibleListener#getName(AccessibleEvent) */
+    public void getName(AccessibleEvent e) {
+        EventDispatcher.AccessibilityDispatcher ad;
+        ad = getEventDispatcher().getAccessibilityDispatcher_package();
+        if (ad !is null)
+            ad.getName(e);
+    }
+
+    /** @see AccessibleControlListener#getRole(AccessibleControlEvent) */
+    public void getRole(AccessibleControlEvent e) {
+        EventDispatcher.AccessibilityDispatcher ad;
+        ad = getEventDispatcher().getAccessibilityDispatcher_package();
+        if (ad !is null)
+            ad.getRole(e);
+    }
+
+    /** @see AccessibleControlListener#getSelection(AccessibleControlEvent) */
+    public void getSelection(AccessibleControlEvent e) {
+        EventDispatcher.AccessibilityDispatcher ad;
+        ad = getEventDispatcher().getAccessibilityDispatcher_package();
+        if (ad !is null)
+            ad.getSelection(e);
+    }
+
+    /** @see AccessibleControlListener#getState(AccessibleControlEvent) */
+    public void getState(AccessibleControlEvent e) {
+        EventDispatcher.AccessibilityDispatcher ad;
+        ad = getEventDispatcher().getAccessibilityDispatcher_package();
+        if (ad !is null)
+            ad.getState(e);
+    }
+
+    /** @see AccessibleControlListener#getValue(AccessibleControlEvent) */
+    public void getValue(AccessibleControlEvent e) {
+        EventDispatcher.AccessibilityDispatcher ad;
+        ad = getEventDispatcher().getAccessibilityDispatcher_package();
+        if (ad !is null)
+            ad.getValue(e);
+    }
+
+    /**
+     * @see Listener#handleEvent(dwt.widgets.Event)
+     * @since 3.1
+     */
+    public void handleEvent(Event event) {
+        // Mouse wheel events
+        if (event.type is DWT.MouseWheel)
+            getEventDispatcher().dispatchMouseWheelScrolled(event);
+    }
+
+    /** @see KeyListener#keyPressed(KeyEvent) */
+    public void keyPressed(KeyEvent e) {
+        getEventDispatcher().dispatchKeyPressed(e);
+    }
+
+    /** @see KeyListener#keyReleased(KeyEvent) */
+    public void keyReleased(KeyEvent e) {
+        getEventDispatcher().dispatchKeyReleased(e);
+    }
+
+    /** @see TraverseListener#keyTraversed(TraverseEvent) */
+    public void keyTraversed(TraverseEvent e) {
+        /*
+         * Doit is almost always false by default for Canvases with KeyListeners. Set to
+         * true to allow normal behavior.  For example, in Dialogs ESC should close.
+         */
+        e.doit = true;
+        getEventDispatcher().dispatchKeyTraversed(e);
+    }
+
+    /** @see MouseListener#mouseDoubleClick(MouseEvent) */
+    public void mouseDoubleClick(MouseEvent e) {
+        getEventDispatcher().dispatchMouseDoubleClicked(e);
+    }
+
+    /**@see MouseListener#mouseDown(MouseEvent)*/
+    public void mouseDown(MouseEvent e) {
+        getEventDispatcher().dispatchMousePressed(e);
+    }
+
+    /**@see MouseTrackListener#mouseEnter(MouseEvent)*/
+    public void mouseEnter(MouseEvent e) {
+        getEventDispatcher().dispatchMouseEntered(e);
+    }
+
+    /**@see MouseTrackListener#mouseExit(MouseEvent)*/
+    public void mouseExit(MouseEvent e) {
+        getEventDispatcher().dispatchMouseExited(e);
+    }
+
+    /**@see MouseTrackListener#mouseHover(MouseEvent)*/
+    public void mouseHover(MouseEvent e) {
+        getEventDispatcher().dispatchMouseHover(e);
+    }
+
+    /**@see MouseMoveListener#mouseMove(MouseEvent)*/
+    public void mouseMove(MouseEvent e) {
+        getEventDispatcher().dispatchMouseMoved(e);
+    }
+
+    /**@see MouseListener#mouseUp(MouseEvent)*/
+    public void mouseUp(MouseEvent e) {
+        getEventDispatcher().dispatchMouseReleased(e);
+    }
+
+    /**@see DisposeListener#widgetDisposed(DisposeEvent)*/
+    public void widgetDisposed(DisposeEvent e) {
+        getUpdateManager().dispose();
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/LineBorder.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.LineBorder;
+
+import dwt.dwthelper.utils;
+
+
+import dwtx.draw2d.AbstractBorder;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Graphics;
+
+import dwt.graphics.Color;
+import dwtx.draw2d.geometry.Insets;
+
+/**
+ * Provides for a line border with sides of equal widths.
+ */
+public class LineBorder
+    : AbstractBorder
+{
+
+private int width = 1;
+private Color color;
+
+/**
+ * Constructs a LineBorder with the specified color and of the specified width.
+ *
+ * @param color The color of the border.
+ * @param width The width of the border in pixels.
+ * @since 2.0
+ */
+public this(Color color, int width) {
+    setColor(color);
+    setWidth(width);
+}
+
+/**
+ * Constructs a LineBorder with the specified color and a width of 1 pixel.
+ *
+ * @param color The color of the border.
+ * @since 2.0
+ */
+public this(Color color) {
+    this(color, 1);
+}
+
+/**
+ * Constructs a black LineBorder with the specified width.
+ *
+ * @param width The width of the border in pixels.
+ * @since 2.0
+ */
+public this(int width) {
+    this(null, width);
+}
+
+/**
+ * Constructs a default black LineBorder with a width of one pixel.
+ *
+ * @since 2.0
+ */
+public this() { }
+
+/**
+ * Returns the line color of this border.
+ * @return The line color of this border
+ */
+public Color getColor() {
+    return color;
+}
+
+/**
+ * Returns the space used by the border for the figure provided as input. In this border
+ * all sides always have equal width.
+ * @param figure The figure this border belongs to
+ * @return This border's insets
+ */
+public Insets getInsets(IFigure figure) {
+    return new Insets(getWidth());
+}
+
+/**
+ * Returns the line width of this border.
+ * @return The line width of this border
+ */
+public int getWidth() {
+    return width;
+}
+
+/**
+ * Returns <code>true</code> since this border is opaque. Being opaque it is responsible
+ * to fill in the area within its boundaries.
+ * @return <code>true</code> since this border is opaque
+ */
+public bool isOpaque() {
+    return true;
+}
+
+/**
+ * @see dwtx.draw2d.Border#paint(IFigure, Graphics, Insets)
+ */
+public void paint(IFigure figure, Graphics graphics, Insets insets) {
+    tempRect.setBounds(getPaintRectangle(figure, insets));
+    if (getWidth() % 2 is 1) {
+        tempRect.width--;
+        tempRect.height--;
+    }
+    tempRect.shrink(getWidth() / 2, getWidth() / 2);
+    graphics.setLineWidth(getWidth());
+    if (getColor() !is null)
+        graphics.setForegroundColor(getColor());
+    graphics.drawRectangle(tempRect);
+}
+
+/**
+ * Sets the line color for this border.
+ * @param color The line color
+ */
+public void setColor(Color color) {
+    this.color = color;
+}
+
+/**
+ * Sets the line width for this border.
+ * @param width The line width
+ */
+public void setWidth(int width) {
+    this.width = width;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Locator.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.Locator;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.IFigure;
+
+/**
+ * Controls the location of an IFigure.
+ */
+public interface Locator {
+
+/**
+ * Relocates the given IFigure.
+ * @param target The figure to relocate
+ */
+void relocate(IFigure target);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ManhattanConnectionRouter.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,394 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ManhattanConnectionRouter;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.geometry.Ray;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.AbstractRouter;
+import dwtx.draw2d.Connection;
+import dwtx.draw2d.ConnectionAnchor;
+
+/**
+ * Provides a {@link Connection} with an orthogonal route between the Connection's source
+ * and target anchors.
+ */
+public final class ManhattanConnectionRouter
+    : AbstractRouter
+{
+
+private Map rowsUsed;
+private Map colsUsed;
+//private Hashtable offsets = new Hashtable(7);
+
+private Map reservedInfo;
+
+private class ReservedInfo {
+    public List reservedRows;
+    public List reservedCols;
+    this(){
+        reservedRows = new ArrayList(2);
+        reservedCols = new ArrayList(2);
+    }
+}
+
+private static Ray  UP, DOWN, LEFT, RIGHT;
+
+
+static this(){
+    UP      = new Ray(0, -1);
+    DOWN    = new Ray(0, 1);
+    LEFT    = new Ray(-1, 0);
+    RIGHT   = new Ray(1, 0);
+}
+
+public this(){
+    rowsUsed = new HashMap();
+    colsUsed = new HashMap();
+    reservedInfo = new HashMap();
+}
+
+/**
+ * @see ConnectionRouter#invalidate(Connection)
+ */
+public void invalidate(Connection connection) {
+    removeReservedLines(connection);
+}
+
+private int getColumnNear(Connection connection, int r, int n, int x) {
+    int min = Math.min(n, x),
+        max = Math.max(n, x);
+    if (min > r) {
+        max = min;
+        min = r - (min - r);
+    }
+    if (max < r) {
+        min = max;
+        max = r + (r - max);
+    }
+    int proximity = 0;
+    int direction = -1;
+    if (r % 2 is 1)
+        r--;
+    Integer i;
+    while (proximity < r) {
+        i = new Integer(r + proximity * direction);
+        if (!colsUsed.containsKey(i)) {
+            colsUsed.put(i, i);
+            reserveColumn(connection, i);
+            return i.intValue();
+        }
+        int j = i.intValue();
+        if (j <= min)
+            return j + 2;
+        if (j >= max)
+            return j - 2;
+        if (direction is 1)
+            direction = -1;
+        else {
+            direction = 1;
+            proximity += 2;
+        }
+    }
+    return r;
+}
+
+/**
+ * Returns the direction the point <i>p</i> is in relation to the given rectangle.
+ * Possible values are LEFT (-1,0), RIGHT (1,0), UP (0,-1) and DOWN (0,1).
+ *
+ * @param r the rectangle
+ * @param p the point
+ * @return the direction from <i>r</i> to <i>p</i>
+ */
+protected Ray getDirection(Rectangle r, Point p) {
+    int i, distance = Math.abs(r.x - p.x);
+    Ray direction;
+
+    direction = LEFT;
+
+    i = Math.abs(r.y - p.y);
+    if (i <= distance) {
+        distance = i;
+        direction = UP;
+    }
+
+    i = Math.abs(r.bottom() - p.y);
+    if (i <= distance) {
+        distance = i;
+        direction = DOWN;
+    }
+
+    i = Math.abs(r.right() - p.x);
+    if (i < distance) {
+        distance = i;
+        direction = RIGHT;
+    }
+
+    return direction;
+}
+
+protected Ray getEndDirection(Connection conn) {
+    ConnectionAnchor anchor = conn.getTargetAnchor();
+    Point p = getEndPoint(conn);
+    Rectangle rect;
+    if (anchor.getOwner() is null)
+        rect = new Rectangle(p.x - 1, p.y - 1, 2, 2);
+    else {
+        rect = conn.getTargetAnchor().getOwner().getBounds().getCopy();
+        conn.getTargetAnchor().getOwner().translateToAbsolute(rect);
+    }
+    return getDirection(rect, p);
+}
+
+protected int getRowNear(Connection connection, int r, int n, int x) {
+    int min = Math.min(n, x),
+        max = Math.max(n, x);
+    if (min > r) {
+        max = min;
+        min = r - (min - r);
+    }
+    if (max < r) {
+        min = max;
+        max = r + (r - max);
+    }
+
+    int proximity = 0;
+    int direction = -1;
+    if (r % 2 is 1)
+        r--;
+    Integer i;
+    while (proximity < r) {
+        i = new Integer(r + proximity * direction);
+        if (!rowsUsed.containsKey(i)) {
+            rowsUsed.put(i, i);
+            reserveRow(connection, i);
+            return i.intValue();
+        }
+        int j = i.intValue();
+        if (j <= min)
+            return j + 2;
+        if (j >= max)
+            return j - 2;
+        if (direction is 1)
+            direction = -1;
+        else {
+            direction = 1;
+            proximity += 2;
+        }
+    }
+    return r;
+}
+
+protected Ray getStartDirection(Connection conn) {
+    ConnectionAnchor anchor = conn.getSourceAnchor();
+    Point p = getStartPoint(conn);
+    Rectangle rect;
+    if (anchor.getOwner() is null)
+        rect = new Rectangle(p.x - 1, p.y - 1, 2, 2);
+    else {
+        rect = conn.getSourceAnchor().getOwner().getBounds().getCopy();
+        conn.getSourceAnchor().getOwner().translateToAbsolute(rect);
+    }
+    return getDirection(rect, p);
+}
+
+protected void processPositions(Ray start, Ray end, List positions,
+                                bool horizontal, Connection conn) {
+    removeReservedLines(conn);
+
+    int pos[] = new int[positions.size() + 2];
+    if (horizontal)
+        pos[0] = start.x;
+    else
+        pos[0] = start.y;
+    int i;
+    for (i = 0; i < positions.size(); i++) {
+        pos[i + 1] = (cast(Integer)positions.get(i)).intValue();
+    }
+    if (horizontal is (positions.size() % 2 is 1))
+        pos[++i] = end.x;
+    else
+        pos[++i] = end.y;
+
+    PointList points = new PointList();
+    points.addPoint(new Point(start.x, start.y));
+    Point p;
+    int current, prev, min, max;
+    bool adjust;
+    for (i = 2; i < pos.length - 1; i++) {
+        horizontal = !horizontal;
+        prev = pos[i - 1];
+        current = pos[i];
+
+        adjust = (i !is pos.length - 2);
+        if (horizontal) {
+            if (adjust) {
+                min = pos[i - 2];
+                max = pos[i + 2];
+                pos[i] = current = getRowNear(conn, current, min, max);
+            }
+            p = new Point(prev, current);
+        } else {
+            if (adjust) {
+                min = pos[i - 2];
+                max = pos[i + 2];
+                pos[i] = current = getColumnNear(conn, current, min, max);
+            }
+            p = new Point(current, prev);
+        }
+        points.addPoint(p);
+    }
+    points.addPoint(new Point(end.x, end.y));
+    conn.setPoints(points);
+}
+
+/**
+ * @see ConnectionRouter#remove(Connection)
+ */
+public void remove(Connection connection) {
+    removeReservedLines(connection);
+}
+
+protected void removeReservedLines(Connection connection) {
+    ReservedInfo rInfo = cast(ReservedInfo) reservedInfo.get(cast(Object)connection);
+    if (rInfo is null)
+        return;
+
+    for (int i = 0; i < rInfo.reservedRows.size(); i++) {
+        rowsUsed.remove(rInfo.reservedRows.get(i));
+    }
+    for (int i = 0; i < rInfo.reservedCols.size(); i++) {
+        colsUsed.remove(rInfo.reservedCols.get(i));
+    }
+    reservedInfo.remove(cast(Object)connection);
+}
+
+protected void reserveColumn(Connection connection, Integer column) {
+    ReservedInfo info = cast(ReservedInfo) reservedInfo.get(cast(Object)connection);
+    if (info is null) {
+        info = new ReservedInfo();
+        reservedInfo.put(cast(Object)connection, info);
+    }
+    info.reservedCols.add(column);
+}
+
+protected void reserveRow(Connection connection, Integer row) {
+    ReservedInfo info = cast(ReservedInfo) reservedInfo.get(cast(Object)connection);
+    if (info is null) {
+        info = new ReservedInfo();
+        reservedInfo.put(cast(Object)connection, info);
+    }
+    info.reservedRows.add(row);
+}
+
+/**
+ * @see ConnectionRouter#route(Connection)
+ */
+public void route(Connection conn) {
+    if ((conn.getSourceAnchor() is null) || (conn.getTargetAnchor() is null))
+        return;
+    int i;
+    Point startPoint = getStartPoint(conn);
+    conn.translateToRelative(startPoint);
+    Point endPoint = getEndPoint(conn);
+    conn.translateToRelative(endPoint);
+
+    Ray start = new Ray(startPoint);
+    Ray end = new Ray(endPoint);
+    Ray average = start.getAveraged(end);
+
+    Ray direction = new Ray(start, end);
+    Ray startNormal = getStartDirection(conn);
+    Ray endNormal   = getEndDirection(conn);
+
+    List positions = new ArrayList(5);
+    bool horizontal = startNormal.isHorizontal();
+    if (horizontal)
+        positions.add(new Integer(start.y));
+    else
+        positions.add(new Integer(start.x));
+    horizontal = !horizontal;
+
+    if (startNormal.dotProduct(endNormal) is 0) {
+        if ((startNormal.dotProduct(direction) >= 0)
+            && (endNormal.dotProduct(direction) <= 0)) {
+            // 0
+        } else {
+            // 2
+            if (startNormal.dotProduct(direction) < 0)
+                i = startNormal.similarity(start.getAdded(startNormal.getScaled(10)));
+            else {
+                if (horizontal)
+                    i = average.y;
+                else
+                    i = average.x;
+            }
+            positions.add(new Integer(i));
+            horizontal = !horizontal;
+
+            if (endNormal.dotProduct(direction) > 0)
+                i = endNormal.similarity(end.getAdded(endNormal.getScaled(10)));
+            else {
+                if (horizontal)
+                    i = average.y;
+                else
+                    i = average.x;
+            }
+            positions.add(new Integer(i));
+            horizontal = !horizontal;
+        }
+    } else {
+        if (startNormal.dotProduct(endNormal) > 0) {
+            //1
+            if (startNormal.dotProduct(direction) >= 0)
+                i = startNormal.similarity(start.getAdded(startNormal.getScaled(10)));
+            else
+                i = endNormal.similarity(end.getAdded(endNormal.getScaled(10)));
+            positions.add(new Integer(i));
+            horizontal = !horizontal;
+        } else {
+            //3 or 1
+            if (startNormal.dotProduct(direction) < 0) {
+                i = startNormal.similarity(start.getAdded(startNormal.getScaled(10)));
+                positions.add(new Integer(i));
+                horizontal = !horizontal;
+            }
+
+            if (horizontal)
+                i = average.y;
+            else
+                i = average.x;
+            positions.add(new Integer(i));
+            horizontal = !horizontal;
+
+            if (startNormal.dotProduct(direction) < 0) {
+                i = endNormal.similarity(end.getAdded(endNormal.getScaled(10)));
+                positions.add(new Integer(i));
+                horizontal = !horizontal;
+            }
+        }
+    }
+    if (horizontal)
+        positions.add(new Integer(end.y));
+    else
+        positions.add(new Integer(end.x));
+
+    processPositions(start, end, positions, startNormal.isHorizontal(), conn);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/MarginBorder.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.MarginBorder;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.AbstractBorder;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Graphics;
+
+/**
+ * A border that provides blank padding.
+ */
+public class MarginBorder
+    : AbstractBorder
+{
+
+/**
+ * This border's insets.
+ */
+protected Insets insets;
+
+/**
+ * Constructs a MarginBorder with dimensions specified by <i>insets</i>.
+ *
+ * @param insets The Insets for the border
+ * @since 2.0
+ */
+public this(Insets insets) {
+    this.insets = insets;
+}
+
+/**
+ * Constructs a MarginBorder with padding specified by the passed values.
+ *
+ * @param t Top padding
+ * @param l Left padding
+ * @param b Bottom padding
+ * @param r Right padding
+ * @since 2.0
+ */
+public this(int t, int l, int b, int r) {
+    this(new Insets(t, l, b, r));
+}
+
+/**
+ * Constructs a MarginBorder with equal padding on all sides.
+ *
+ * @param allsides Padding size for all sides of the border.
+ * @since 2.0
+ */
+public this(int allsides) {
+    this(new Insets(allsides));
+}
+/**
+ * @see dwtx.draw2d.Border#getInsets(IFigure)
+ */
+public Insets getInsets(IFigure figure) {
+    return insets;
+}
+
+/**
+ * This method does nothing, since this border is just for spacing.
+ * @see dwtx.draw2d.Border#paint(IFigure, Graphics, Insets)
+ */
+public void paint(IFigure figure, Graphics graphics, Insets insets) { }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/MidpointLocator.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.MidpointLocator;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.ConnectionLocator;
+import dwtx.draw2d.Connection;
+
+/**
+ * A ConnectionLocator that is used to place figures at the midpoint between two points on
+ * a {@link dwtx.draw2d.Connection}.
+ */
+public class MidpointLocator
+    : ConnectionLocator
+{
+
+private int index;
+
+/**
+ * Constructs a MidpointLocator with associated Connection <i>c</i> and index <i>i</i>.
+ * The points at index i and i+1 on the connection are used to calculate the midpoint of
+ * the line segment.
+ *
+ * @param c the connection associated with the locator
+ * @param i the point from where the connection's midpoint will be calculated.
+ * @since 2.0
+ */
+public this(Connection c, int i) {
+    super(c);
+    index = i;
+}
+
+/**
+ * Returns this MidpointLocator's index. This integer represents the position of the start
+ * point in this MidpointLocator's associated {@link Connection} from where midpoint
+ * calculation will be made.
+ *
+ * @return the locator's index
+ * @since 2.0
+ */
+
+protected int getIndex() {
+    return index;
+}
+
+/**
+ * Returns the point of reference associated with this locator. This point will be midway
+ * between points at 'index' and 'index' + 1.
+ *
+ * @return the reference point
+ * @since 2.0
+ */
+protected Point getReferencePoint() {
+    Connection conn = getConnection();
+    Point p = Point.SINGLETON;
+    Point p1 = conn.getPoints().getPoint(getIndex());
+    Point p2 = conn.getPoints().getPoint(getIndex() + 1);
+    conn.translateToAbsolute(p1);
+    conn.translateToAbsolute(p2);
+    p.x = (p2.x - p1.x) / 2 + p1.x;
+    p.y = (p2.y - p1.y) / 2 + p1.y;
+    return p;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/MouseEvent.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.MouseEvent;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import tango.text.convert.Format;
+import dwtx.draw2d.InputEvent;
+import dwtx.draw2d.EventDispatcher;
+import dwtx.draw2d.IFigure;
+
+/**
+ * An event caused by the user interacting with the mouse.
+ */
+public class MouseEvent
+    : InputEvent
+{
+
+/** The X coordinate of the mouse event. */
+public int x;
+/** The Y coordinate of the mouse event. */
+public int y;
+
+/** The button that was pressed or released: {1, 2, 3}. */
+public int button;
+
+this(int x, int y, EventDispatcher dispatcher,
+            IFigure f, int button, int stateMask) {
+    super(dispatcher, f, stateMask);
+    Point pt = Point.SINGLETON;
+    pt.setLocation(x, y);
+    f.translateToRelative(pt);
+    this.button = button;
+    this.x = pt.x;
+    this.y = pt.y;
+}
+
+/**
+ * @return the location of this mouse event
+ */
+public Point getLocation() {
+    return new Point(x, y);
+}
+
+/**
+ * @see Object#toString()
+ */
+public override String toString() {
+    return Format( "MouseEvent({},{}) to Figure: {}", x, y, source);//$NON-NLS-2$//$NON-NLS-1$
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/MouseListener.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.MouseListener;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.MouseEvent;
+
+/**
+ * A listener interface for receiving mouse button events.
+ */
+public interface MouseListener {
+
+/**
+ * Called when a mouse button has been pressed while over the listened to object.
+ * @param me The MouseEvent object
+ */
+void mousePressed(MouseEvent me);
+
+/**
+ * Called when a pressed mouse button has been released.
+ * @param me The MouseEvent object
+ */
+void mouseReleased(MouseEvent me);
+
+/**
+ * Called when a mouse button has been double clicked over the listened to object.
+ * @param me The MouseEvent object
+ */
+void mouseDoubleClicked(MouseEvent me);
+
+/**
+ * An empty implementation of MouseListener for convenience.
+ */
+public class Stub
+    : MouseListener
+{
+    /**
+     * @see dwtx.draw2d.MouseListener#mousePressed(MouseEvent)
+     */
+    public void mousePressed(MouseEvent me) { }
+    /**
+     * @see dwtx.draw2d.MouseListener#mouseReleased(MouseEvent)
+     */
+    public void mouseReleased(MouseEvent me) { }
+    /**
+     * @see dwtx.draw2d.MouseListener#mouseDoubleClicked(MouseEvent)
+     */
+    public void mouseDoubleClicked(MouseEvent me) { }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/MouseMotionListener.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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 dwtx.draw2d.MouseMotionListener;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.MouseEvent;
+/**
+ * A listener interface for receiving mouse motion events.
+ */
+public interface MouseMotionListener {
+
+/**
+ * Called when the mouse has moved over the listened to object while a button was pressed.
+ * @param me The MouseEvent object
+ */
+void mouseDragged(MouseEvent me);
+
+/**
+ * Called when the mouse has entered the listened to object.
+ * @param me The MouseEvent object
+ */
+void mouseEntered(MouseEvent me);
+
+/**
+ * Called when the mouse has exited the listened to object.
+ * @param me The MouseEvent object
+ */
+void mouseExited(MouseEvent me);
+
+/**
+ * Called when the mouse hovers over the listened to object.
+ * @param me The MouseEvent object
+ */
+void mouseHover(MouseEvent me);
+
+/**
+ * Called when the mouse has moved over the listened to object.
+ * @param me The MouseEvent object
+ */
+void mouseMoved(MouseEvent me);
+
+/**
+ * An empty implementation of MouseMotionListener for convenience.
+ */
+static public class Stub
+    : MouseMotionListener
+{
+    /**
+     * @see dwtx.draw2d.MouseMotionListener#mouseDragged(MouseEvent)
+     */
+    public void mouseDragged(MouseEvent me) { }
+    /**
+     * @see dwtx.draw2d.MouseMotionListener#mouseEntered(MouseEvent)
+     */
+    public void mouseEntered(MouseEvent me) { }
+    /**
+     * @see dwtx.draw2d.MouseMotionListener#mouseExited(MouseEvent)
+     */
+    public void mouseExited(MouseEvent me) { }
+    /**
+     * @see dwtx.draw2d.MouseMotionListener#mouseMoved(MouseEvent)
+     */
+    public void mouseMoved(MouseEvent me) { }
+    /**
+     * @see dwtx.draw2d.MouseMotionListener#mouseHover(MouseEvent)
+     */
+    public void mouseHover(MouseEvent me) { }
+}
+
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/NativeGraphicsSource.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.NativeGraphicsSource;
+
+import dwt.dwthelper.utils;
+
+
+
+import dwt.widgets.Control;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.GraphicsSource;
+import dwtx.draw2d.Graphics;
+
+/**
+ * A graphics source that posts a paint request to the control rather than constructing GC
+ * on it directly. This allows the OS's native painting mechanism to be used directly,
+ * including any double-buffering that the OS may provide for free.
+ * @since 3.2
+ */
+public final class NativeGraphicsSource : GraphicsSource {
+
+private final Control canvas;
+
+/**
+ * Constructs a new graphics source on the given control.
+ * @param canvas the control
+ * @since 3.2
+ */
+public this(Control canvas) {
+    this.canvas = canvas;
+}
+
+/**
+ * Always returns <code>null</code>, because
+ * @see GraphicsSource#getGraphics(Rectangle)
+ */
+public Graphics getGraphics(Rectangle r) {
+    canvas.redraw(r.x, r.y, r.width, r.height, false);
+    canvas.update();
+    return null;
+}
+
+/**
+ * Does nothing.
+ * @see GraphicsSource#flushGraphics(Rectangle)
+ */
+public void flushGraphics(Rectangle region) { }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Orientable.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.Orientable;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.PositionConstants;
+
+/**
+ * An interface for objects that can be either horizontally or vertically oriented.
+ */
+public interface Orientable
+    : IFigure, PositionConstants
+{
+
+/**
+ * A constant representing a horizontal orientation.
+ */
+static const int HORIZONTAL = 0;
+/**
+ * A constant representing a vertical orientation.
+ */
+static const int VERTICAL = 1;
+
+/**
+ * Sets the orientation. Can be either {@link #HORIZONTAL} or {@link #VERTICAL}.
+ * @param orientation The orientation
+ */
+void setOrientation(int orientation);
+
+/**
+ * Sets the direction the orientable figure will face.  Can be one of many directional
+ * constants defined in {@link PositionConstants}.
+ * @param direction The direction
+ */
+void setDirection(int direction);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Panel.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.Panel;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.Figure;
+
+/**
+ * A General purpose Container. This figure is opaque by default, and will fill its entire
+ * bounds with either the background color that is set on the figure, or the IGraphics
+ * current background color if none has been set.  Opaque figures help to optimize
+ * painting.
+ * <p>
+ * Note that the paintFigure() method in the superclass Figure actually fills the bounds
+ * of this figure.
+ */
+public class Panel
+    : Figure
+{
+
+/**
+ * Returns <code>true</code> as this is an opaque figure.
+ *
+ * @return the opaque state of this figure
+ * @since 2.0
+ */
+public bool isOpaque() {
+    return true;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Polygon.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ *     Alex Selkov - Fix for Bug# 22701
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.Polygon;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.draw2d.Polyline;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.IFigure;
+
+/**
+ * Renders a {@link dwtx.draw2d.geometry.PointList} as a polygonal shape.
+ * This class is similar to Polyline, except the PointList is closed and can be filled in
+ * as a solid shape.
+ * @see Polyline
+ */
+public class Polygon
+    : Polyline
+{
+
+/**
+ * Returns whether the point (x,y) is contained inside this polygon.
+ * @param x the X coordinate
+ * @param y the Y coordinate
+ * @return whether the point (x,y) is contained in this polygon
+ */
+public bool containsPoint(int x, int y) {
+    if (!getBounds().contains(x, y))
+        return false;
+
+    bool isOdd = false;
+    int[] pointsxy = getPoints().toIntArray();
+    int n = pointsxy.length;
+    if (n > 3) { //If there are at least 2 Points (4 ints)
+        int x1, y1;
+        int x0 = pointsxy[n - 2];
+        int y0 = pointsxy[n - 1];
+
+        for (int i = 0; i < n; x0 = x1, y0 = y1) {
+            x1 = pointsxy[i++];
+            y1 = pointsxy[i++];
+
+            if (y0 <= y && y < y1
+              && crossProduct(x1, y1, x0, y0, x, y) > 0)
+                isOdd = !isOdd;
+            if (y1 <= y && y < y0
+              && crossProduct(x0, y0, x1, y1, x, y) > 0)
+                isOdd = !isOdd;
+        }
+        if (isOdd)
+            return true;
+    }
+
+    List children = getChildren();
+    for (int i = 0; i < children.size(); i++)
+        if ((cast(IFigure) children.get(i)).containsPoint(x, y))
+            return true;
+
+    return false;
+}
+
+private int crossProduct(int ax, int ay, int bx, int by, int cx, int cy) {
+    return (ax - cx) * (by - cy) - (ay - cy) * (bx - cx);
+}
+
+/**
+ * Fill the Polygon with the background color set by <i>g</i>.
+ *
+ * @param g the Graphics object
+ * @since 2.0
+ */
+protected void fillShape(Graphics g) {
+    g.fillPolygon(getPoints());
+}
+
+/**
+ * Draw the outline of the Polygon.
+ *
+ * @param g the Graphics object
+ * @since 2.0
+ */
+protected void outlineShape(Graphics g) {
+    g.drawPolygon(getPoints());
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/PolygonDecoration.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.PolygonDecoration;
+
+import dwt.dwthelper.utils;
+
+
+
+import dwt.graphics.Color;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.geometry.Transform;
+import dwtx.draw2d.RotatableDecoration;
+import dwtx.draw2d.Polygon;
+
+/**
+ * A rotatable, polygon shaped decoration most commonly used for decorating the ends of
+ * {@link dwtx.draw2d.Polyline polylines}.
+ */
+public class PolygonDecoration
+    : Polygon
+    , RotatableDecoration
+{
+
+/** Template for a triangle that points to the right when the rotation angle is 0 */
+public static const PointList TRIANGLE_TIP;
+/** Template for a triangle that points to the left when the rotation angle is 0 */
+public static const PointList INVERTED_TRIANGLE_TIP;
+
+static this() {
+    TRIANGLE_TIP = new PointList();
+    INVERTED_TRIANGLE_TIP = new PointList();
+    TRIANGLE_TIP.addPoint(0, 0);
+    TRIANGLE_TIP.addPoint(-1, 1);
+    TRIANGLE_TIP.addPoint(-1, -1);
+
+    INVERTED_TRIANGLE_TIP.addPoint(0, 1);
+    INVERTED_TRIANGLE_TIP.addPoint(0, -1);
+    INVERTED_TRIANGLE_TIP.addPoint(-1, 0);
+}
+
+private Point location;
+private PointList template_;
+private Transform transform;
+
+/**
+ * Constructs a PolygonDecoration. Defaults the PolygonDecoration to fill its region
+ * with black.
+ *
+ * @since 2.0
+ */
+public this() {
+    location = new Point();
+    template_ = TRIANGLE_TIP;
+    transform = new Transform();
+    setFill(true);
+    setScale(7, 3);
+}
+
+/**
+ * @see dwtx.draw2d.IFigure#getBackgroundColor()
+ */
+public Color getLocalBackgroundColor() {
+    if (super.getLocalBackgroundColor() is null)
+        return getForegroundColor();
+    return super.getLocalBackgroundColor();
+}
+
+/**
+ * Returns the points in the PolygonDecoration as a PointList.
+ *
+ * @return the points in this PolygonDecoration
+ * @since 2.0
+ */
+public PointList getPoints() {
+    if (points is null) {
+        points = new PointList();
+        for (int i = 0; i < template_.size(); i++)
+            points.addPoint(transform.getTransformed(template_.getPoint(i)));
+    }
+    return points;
+}
+
+/**
+ * Sets the location of this PolygonDecoration.
+ * @param p the new location
+ */
+public void setLocation(Point p) {
+    points = null;
+    bounds = null;
+    location.setLocation(p);
+    transform.setTranslation(p.x, p.y);
+}
+
+/**
+ * Sets the PolygonDecorations point template_ to the passed PointList. This template_ is an
+ * outline of the PolygonDecoration's region. (The default value is TRIANGLE_TIP which is
+ * a triangle whose tip is pointing to the right).
+ *
+ * @param pl the PointList outline to use as the PolygonDecoration's region
+ * @since 2.0
+ */
+public void setTemplate(PointList pl) {
+    erase();
+    template_ = pl;
+    points = null;
+    bounds = null;
+    repaint();
+}
+
+/**
+ * Sets the amount of scaling to be done along X and Y axes on the PolygonDecoration's
+ * template_.
+ *
+ * @param x X scaling
+ * @param y Y scaling
+ * @since 2.0
+ */
+public void setScale(double x, double y) {
+    points = null;
+    bounds = null;
+    transform.setScale(x, y);
+}
+
+/**
+ * Sets the rotation of this decoration so that the decoration points toward the
+ * given reference point.
+ * @param ref the reference point
+ */
+public void setReferencePoint(Point ref_) {
+    Point pt = Point.SINGLETON;
+    pt.setLocation(ref_);
+    pt.negate().translate(location);
+    setRotation(Math.atan2(pt.y, pt.x));
+}
+
+/**
+ * Sets the angle by which rotation is to be done on the PolygonDecoration.
+ *
+ * @param angle Angle of rotation
+ * @since 2.0
+ */
+public void setRotation(double angle) {
+    points = null;
+    bounds = null;
+    transform.setRotation(angle);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Polyline.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,317 @@
+/*******************************************************************************
+ * 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.Polyline;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.Shape;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Connection;
+
+/**
+ * Renders a {@link PointList} as a series of line segments.  A Polyline figure should be
+ * positioned by manipulating its points, <EM>NOT</EM> by calling
+ * {@link Figure#setBounds(Rectangle)}.
+ * <P>
+ * A polyline's bounds will be calculated automatically based on its PointList.  The
+ * bounds will be the smallest Rectangle large enough to render the line properly.
+ * Children should not be added to a Polyline and will not affect the bounds calculation.
+ */
+public class Polyline
+    : Shape
+{
+
+PointList points;
+private int tolerance = 2;
+private static const Rectangle LINEBOUNDS;
+
+static this(){
+    LINEBOUNDS = Rectangle.SINGLETON;
+    assert( LINEBOUNDS !is null );
+}
+
+this(){
+    points = new PointList();
+    setFill(false);
+    bounds  = null;
+}
+
+/**
+ * Adds the passed point to the Polyline.
+ *
+ * @param pt the Point to be added to the Polyline
+ * @since 2.0
+ */
+public void addPoint(Point pt) {
+    points.addPoint(pt);
+    bounds = null;
+    repaint();
+}
+
+/**
+ * @see dwtx.draw2d.IFigure#containsPoint(int, int)
+ */
+public bool containsPoint(int x, int y) {
+    int tolerance = Math.max(lineWidth / 2, this.tolerance);
+    LINEBOUNDS.setBounds(getBounds());
+    LINEBOUNDS.expand(tolerance, tolerance);
+    if (!LINEBOUNDS.contains(x, y))
+        return false;
+    int ints[] = points.toIntArray();
+    for (int index = 0; index < ints.length - 3; index  += 2) {
+        if (lineContainsPoint(ints[index], ints[index + 1],
+            ints[index + 2], ints[index + 3], x, y, tolerance))
+            return true;
+    }
+    List children = getChildren();
+    for (int i = 0; i < children.size(); i++) {
+        if ((cast(IFigure)children.get(i)).containsPoint(x, y))
+            return true;
+    }
+    return false;
+}
+
+private bool lineContainsPoint(
+    int x1, int y1,
+    int x2, int y2,
+    int px, int py,
+    int tolerance) {
+    LINEBOUNDS.setSize(0, 0);
+    LINEBOUNDS.setLocation(x1, y1);
+    LINEBOUNDS.union_(x2, y2);
+    LINEBOUNDS.expand(tolerance, tolerance);
+    if (!LINEBOUNDS.contains(px, py))
+        return false;
+
+    int v1x, v1y, v2x, v2y;
+    int numerator, denominator;
+    int result = 0;
+
+    /**
+     * calculates the length squared of the cross product of two vectors, v1 & v2.
+     */
+    if (x1 !is x2 && y1 !is y2) {
+        v1x = x2 - x1;
+        v1y = y2 - y1;
+        v2x = px - x1;
+        v2y = py - y1;
+
+        numerator = v2x * v1y - v1x * v2y;
+
+        denominator = v1x * v1x + v1y * v1y;
+
+        result = cast(int)(cast(long)numerator * numerator / denominator);
+    }
+
+    // if it is the same point, and it passes the bounding box test,
+    // the result is always true.
+    return result <= tolerance * tolerance;
+
+}
+
+/**
+ * Null implementation for a line.
+ * @see dwtx.draw2d.Shape#fillShape(Graphics)
+ */
+protected void fillShape(Graphics g) { }
+
+/**
+ * @see dwtx.draw2d.IFigure#getBounds()
+ */
+public Rectangle getBounds() {
+    if (bounds is null) {
+        bounds = getPoints()
+            .getBounds()
+            .getExpanded(lineWidth / 2, lineWidth / 2);
+    }
+    return bounds;
+}
+
+/**
+ * Returns the last point in the Polyline.
+ * @since 2.0
+ * @return the last point
+ */
+public Point getEnd() {
+    return points.getLastPoint();
+}
+
+/**
+ * Returns the points in this Polyline <B>by reference</B>. If the returned list is
+ * modified, this Polyline must be informed by calling {@link #setPoints(PointList)}.
+ * Failure to do so will result in layout and paint problems.
+ *
+ * @return this Polyline's points
+ * @since 2.0
+ */
+public PointList getPoints() {
+    return points;
+}
+
+/**
+ * @return the first point in the Polyline
+ * @since 2.0
+ */
+public Point getStart() {
+    return points.getFirstPoint();
+}
+
+/**
+ * Inserts a given point at a specified index in the Polyline.
+ *
+ * @param pt the point to be added
+ * @param index the position in the Polyline where the point is to be added
+ *
+ * @since 2.0
+ */
+public void insertPoint(Point pt, int index) {
+    bounds = null;
+    points.insertPoint(pt, index);
+    repaint();
+}
+
+/**
+ * @return <code>false</code> because Polyline's aren't filled
+ */
+public bool isOpaque() {
+    return false;
+}
+
+/**
+ * @see Shape#outlineShape(Graphics)
+ */
+protected void outlineShape(Graphics g) {
+    g.drawPolyline(points);
+}
+
+/**
+ * @see Figure#primTranslate(int, int)
+ */
+public void primTranslate(int x, int y) { }
+
+/**
+ * Erases the Polyline and removes all of its {@link Point Points}.
+ *
+ * @since 2.0
+ */
+public void removeAllPoints() {
+    erase();
+    bounds = null;
+    points.removeAllPoints();
+}
+
+/**
+ * Removes a point from the Polyline.
+ *
+ * @param index the position of the point to be removed
+ * @since 2.0
+ */
+public void removePoint(int index) {
+    erase();
+    bounds = null;
+    points.removePoint(index);
+}
+
+/**
+ * Sets the end point of the Polyline
+ *
+ * @param end the point that will become the last point in the Polyline
+ * @since 2.0
+ */
+public void setEnd(Point end) {
+    if (points.size() < 2)
+        addPoint(end);
+    else
+        setPoint(end, points.size() - 1);
+}
+
+/**
+ * Sets the points at both extremes of the Polyline
+ *
+ * @param start the point to become the first point in the Polyline
+ * @param end the point to become the last point in the Polyline
+ * @since 2.0
+ */
+public void setEndpoints(Point start, Point end) {
+    setStart(start);
+    setEnd(end);
+}
+
+/**
+ * @see dwtx.draw2d.Shape#setLineWidth(int)
+ */
+public void setLineWidth(int w) {
+    if (lineWidth is w)
+        return;
+    if (w < lineWidth) //The bounds will become smaller, so erase must occur first.
+        erase();
+    bounds = null;
+    super.setLineWidth(w);
+}
+
+/**
+ * Sets the point at <code>index</code> to the Point <code>pt</code>.  Calling this method
+ * results in a recalculation of the polyline's bounding box.  If you're going to set
+ * multiple Points, use {@link #setPoints(PointList)}.
+ * @param pt the point
+ * @param index the index
+ */
+public void setPoint(Point pt, int index) {
+    erase();
+    points.setPoint(pt, index);
+    bounds = null;
+    repaint();
+}
+
+/**
+ * Sets the list of points to be used by this polyline connection. Removes any previously
+ * existing points. The polyline will hold onto the given list by reference.
+ *
+ * @param points new set of points
+ * @since 2.0
+ */
+public void setPoints(PointList points) {
+    erase();
+    this.points = points;
+    bounds = null;
+    firePropertyChange(Connection.PROPERTY_POINTS, null, points);
+    repaint();
+}
+
+/**
+ * Sets the start point of the Polyline
+ *
+ * @param start the point that will become the first point in the Polyline
+ * @since 2.0
+ */
+public void setStart(Point start) {
+    if (points.size() is 0)
+        addPoint(start);
+    else
+        setPoint(start, 0);
+}
+
+/**
+  * Sets the tolerance
+  *
+  * @param tolerance the new tolerance value of the Polyline
+  */
+public void setTolerance(int tolerance) {
+       this.tolerance = tolerance;
+}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/PolylineConnection.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,395 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.PolylineConnection;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.Polyline;
+import dwtx.draw2d.Connection;
+import dwtx.draw2d.ConnectionAnchor;
+import dwtx.draw2d.AnchorListener;
+import dwtx.draw2d.ConnectionRouter;
+import dwtx.draw2d.ConnectionLocator;
+import dwtx.draw2d.RotatableDecoration;
+import dwtx.draw2d.RoutingListener;
+import dwtx.draw2d.DelegatingLayout;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.ArrowLocator;
+
+/**
+ * An implementation of {@link Connection} based on Polyline.  PolylineConnection adds
+ * the following additional features:
+ * <UL>
+ * <LI>
+ *   A {@link ConnectionRouter} may be provided which will be used to determine the
+ *   connections points.
+ * <LI>
+ *   Children may be added. The bounds calculation is extended such that the bounds is
+ *   the smallest Rectangle which is large enough to display the Polyline and all of its
+ *   children figures.
+ * <LI>
+ *   A {@link DelegatingLayout} is set as the default layout.  A delegating layout allows
+ *   children to position themselves via {@link Locator Locators}.
+ * </UL>
+ * <P>
+ */
+public class PolylineConnection
+    : Polyline
+    , Connection, AnchorListener
+{
+
+// reimplement for Connection
+PointList getPoints(){
+    return super.getPoints();
+}
+// reimplement for Connection
+void setPoints(PointList list){
+    super.setPoints(list);
+}
+
+
+private ConnectionAnchor startAnchor, endAnchor;
+private ConnectionRouter connectionRouter;
+private RotatableDecoration startArrow, endArrow;
+
+private void instanceInit(){
+    connectionRouter = ConnectionRouter_NULL;
+    setLayoutManager(new DelegatingLayout());
+    addPoint(new Point(0, 0));
+    addPoint(new Point(100, 100));
+}
+
+this(){
+    instanceInit();
+}
+/**
+ * Hooks the source and target anchors.
+ * @see Figure#addNotify()
+ */
+public void addNotify() {
+    super.addNotify();
+    hookSourceAnchor();
+    hookTargetAnchor();
+}
+
+/**
+ * Appends the given routing listener to the list of listeners.
+ * @param listener the routing listener
+ * @since 3.2
+ */
+public void addRoutingListener(RoutingListener listener) {
+    if (auto notifier = cast(RoutingNotifier)connectionRouter ) {
+        notifier.listeners.add(cast(Object)listener);
+    } else
+        connectionRouter = new RoutingNotifier(connectionRouter, listener);
+}
+
+/**
+ * Called by the anchors of this connection when they have moved, revalidating this
+ * polyline connection.
+ * @param anchor the anchor that moved
+ */
+public void anchorMoved(ConnectionAnchor anchor) {
+    revalidate();
+}
+
+/**
+ * Returns the bounds which holds all the points in this polyline connection. Returns any
+ * previously existing bounds, else calculates by unioning all the children's
+ * dimensions.
+ * @return the bounds
+ */
+public Rectangle getBounds() {
+    if (bounds is null) {
+        super.getBounds();
+        for (int i = 0; i < getChildren().size(); i++) {
+            IFigure child = cast(IFigure)getChildren().get(i);
+            bounds.union_(child.getBounds());
+        }
+    }
+    return bounds;
+}
+
+/**
+ * Returns the <code>ConnectionRouter</code> used to layout this connection. Will not
+ * return <code>null</code>.
+ * @return this connection's router
+ */
+public ConnectionRouter getConnectionRouter() {
+    if (auto n = cast(RoutingNotifier)connectionRouter )
+        return n.realRouter;
+    return connectionRouter;
+}
+
+/**
+ * Returns this connection's routing constraint from its connection router.  May return
+ * <code>null</code>.
+ * @return the connection's routing constraint
+ */
+public Object getRoutingConstraint() {
+    if (getConnectionRouter() !is null)
+        return getConnectionRouter().getConstraint(this);
+    else
+        return null;
+}
+
+/**
+ * @return the anchor at the start of this polyline connection (may be null)
+ */
+public ConnectionAnchor getSourceAnchor() {
+    return startAnchor;
+}
+
+/**
+ * @return the source decoration (may be null)
+ */
+protected RotatableDecoration getSourceDecoration() {
+    return startArrow;
+}
+
+/**
+ * @return the anchor at the end of this polyline connection (may be null)
+ */
+public ConnectionAnchor getTargetAnchor() {
+    return endAnchor;
+}
+
+/**
+ * @return the target decoration (may be null)
+ *
+ * @since 2.0
+ */
+protected RotatableDecoration getTargetDecoration() {
+    return endArrow;
+}
+
+private void hookSourceAnchor() {
+    if (getSourceAnchor() !is null)
+        getSourceAnchor().addAnchorListener(this);
+}
+
+private void hookTargetAnchor() {
+    if (getTargetAnchor() !is null)
+        getTargetAnchor().addAnchorListener(this);
+}
+
+/**
+ * Layouts this polyline. If the start and end anchors are present, the connection router
+ * is used to route this, after which it is laid out. It also fires a moved method.
+ */
+public void layout() {
+    if (getSourceAnchor() !is null && getTargetAnchor() !is null)
+        connectionRouter.route(this);
+
+    Rectangle oldBounds = bounds;
+    super.layout();
+    bounds = null;
+
+    if (!getBounds().contains(oldBounds)) {
+        getParent().translateToParent(oldBounds);
+        getUpdateManager().addDirtyRegion(getParent(), oldBounds);
+    }
+
+    repaint();
+    fireFigureMoved();
+}
+
+/**
+ * Called just before the receiver is being removed from its parent. Results in removing
+ * itself from the connection router.
+ *
+ * @since 2.0
+ */
+public void removeNotify() {
+    unhookSourceAnchor();
+    unhookTargetAnchor();
+    connectionRouter.remove(this);
+    super.removeNotify();
+}
+
+/**
+ * Removes the first occurence of the given listener.
+ * @param listener the listener being removed
+ * @since 3.2
+ */
+public void removeRoutingListener(RoutingListener listener) {
+    if ( auto notifier = cast(RoutingNotifier)connectionRouter ) {
+        notifier.listeners.remove(cast(Object)listener);
+        if (notifier.listeners.isEmpty())
+            connectionRouter = notifier.realRouter;
+    }
+}
+
+/**
+ * @see IFigure#revalidate()
+ */
+public void revalidate() {
+    super.revalidate();
+    connectionRouter.invalidate(this);
+}
+
+/**
+ * Sets the connection router which handles the layout of this polyline. Generally set by
+ * the parent handling the polyline connection.
+ * @param cr the connection router
+ */
+public void setConnectionRouter(ConnectionRouter cr) {
+    if (cr is null)
+        cr = ConnectionRouter_NULL;
+    ConnectionRouter oldRouter = getConnectionRouter();
+    if (oldRouter !is cr) {
+        connectionRouter.remove(this);
+        if (auto n = cast(RoutingNotifier)connectionRouter )
+            n.realRouter = cr;
+        else
+            connectionRouter = cr;
+        firePropertyChange(Connection.PROPERTY_CONNECTION_ROUTER, cast(Object)oldRouter, cast(Object)cr);
+        revalidate();
+    }
+}
+
+/**
+ * Sets the routing constraint for this connection.
+ * @param cons the constraint
+ */
+public void setRoutingConstraint(Object cons) {
+    if (connectionRouter !is null)
+        connectionRouter.setConstraint(this, cons);
+    revalidate();
+}
+
+/**
+ * Sets the anchor to be used at the start of this polyline connection.
+ * @param anchor the new source anchor
+ */
+public void setSourceAnchor(ConnectionAnchor anchor) {
+    if (anchor is startAnchor)
+        return;
+    unhookSourceAnchor();
+    //No longer needed, revalidate does this.
+    //getConnectionRouter().invalidate(this);
+    startAnchor = anchor;
+    if (getParent() !is null)
+        hookSourceAnchor();
+    revalidate();
+}
+
+/**
+ * Sets the decoration to be used at the start of the {@link Connection}.
+ * @param dec the new source decoration
+ * @since 2.0
+ */
+public void setSourceDecoration(RotatableDecoration dec) {
+    if (startArrow is dec)
+        return;
+    if (startArrow !is null)
+        remove(startArrow);
+    startArrow = dec;
+    if (startArrow !is null)
+        add(startArrow, new ArrowLocator(this, ConnectionLocator.SOURCE));
+}
+
+/**
+ * Sets the anchor to be used at the end of the polyline connection. Removes this listener
+ * from the old anchor and adds it to the new anchor.
+ * @param anchor the new target anchor
+ */
+public void setTargetAnchor(ConnectionAnchor anchor) {
+    if (anchor is endAnchor)
+        return;
+    unhookTargetAnchor();
+    //No longer needed, revalidate does this.
+    //getConnectionRouter().invalidate(this);
+    endAnchor = anchor;
+    if (getParent() !is null)
+        hookTargetAnchor();
+    revalidate();
+}
+
+/**
+ * Sets the decoration to be used at the end of the {@link Connection}.
+ * @param dec the new target decoration
+ */
+public void setTargetDecoration(RotatableDecoration dec) {
+    if (endArrow is dec)
+        return;
+    if (endArrow !is null)
+        remove(endArrow);
+    endArrow = dec;
+    if (endArrow !is null)
+        add(endArrow, new ArrowLocator(this, ConnectionLocator.TARGET));
+}
+
+private void unhookSourceAnchor() {
+    if (getSourceAnchor() !is null)
+        getSourceAnchor().removeAnchorListener(this);
+}
+
+private void unhookTargetAnchor() {
+    if (getTargetAnchor() !is null)
+        getTargetAnchor().removeAnchorListener(this);
+}
+
+final class RoutingNotifier : ConnectionRouter {
+
+    ConnectionRouter realRouter;
+    List listeners;
+
+    this(ConnectionRouter router, RoutingListener listener) {
+        listeners = new ArrayList(1);
+        realRouter = router;
+        listeners.add(cast(Object)listener);
+    }
+
+    public Object getConstraint(Connection connection) {
+        return realRouter.getConstraint(connection);
+    }
+
+    public void invalidate(Connection connection) {
+        for (int i = 0; i < listeners.size(); i++)
+            (cast(RoutingListener)listeners.get(i)).invalidate(connection);
+
+        realRouter.invalidate(connection);
+    }
+
+    public void route(Connection connection) {
+        bool consumed = false;
+        for (int i = 0; i < listeners.size(); i++)
+            consumed |= (cast(RoutingListener)listeners.get(i)).route(connection);
+
+        if (!consumed)
+            realRouter.route(connection);
+
+        for (int i = 0; i < listeners.size(); i++)
+            (cast(RoutingListener)listeners.get(i)).postRoute(connection);
+    }
+
+    public void remove(Connection connection) {
+        for (int i = 0; i < listeners.size(); i++)
+            (cast(RoutingListener)listeners.get(i)).remove(connection);
+        realRouter.remove(connection);
+    }
+
+    public void setConstraint(Connection connection, Object constraint) {
+        for (int i = 0; i < listeners.size(); i++)
+            (cast(RoutingListener)listeners.get(i)).setConstraint(connection, constraint);
+        realRouter.setConstraint(connection, constraint);
+    }
+
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/PolylineDecoration.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.PolylineDecoration;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.geometry.Transform;
+import dwtx.draw2d.Polyline;
+import dwtx.draw2d.RotatableDecoration;
+import dwtx.draw2d.ColorConstants;
+
+/**
+ * A decorative Figure intended to be placed on a {@link Polyline}. It has the default
+ * shape of right-pointing triangle.
+ */
+public class PolylineDecoration
+    : Polyline
+    , RotatableDecoration
+{
+
+/** A triangle template_ */
+public static const PointList TRIANGLE_TIP;
+
+static this() {
+    TRIANGLE_TIP = new PointList();
+    TRIANGLE_TIP.addPoint(-1, 1);
+    TRIANGLE_TIP.addPoint(0, 0);
+    TRIANGLE_TIP.addPoint(-1, -1);
+}
+
+private Point location;
+private PointList template_;
+private Transform transform;
+
+/**
+ * Constructs a PolylineDecoration. Defaults the PolylineDecoration to fill its region
+ * with black.
+ *
+ * @since 2.0
+ */
+public this() {
+    location = new Point();
+    template_ = TRIANGLE_TIP;
+    transform = new Transform();
+    setBackgroundColor(ColorConstants.black);
+    setScale(7, 3);
+}
+
+/**
+ * @see Polyline#getPoints()
+ */
+public PointList getPoints() {
+    if (points is null) {
+        points = new PointList();
+        for (int i = 0; i < template_.size(); i++)
+            points.addPoint(transform.getTransformed(template_.getPoint(i)));
+    }
+    return points;
+}
+
+/**
+ * @see IFigure#setLocation(Point)
+ */
+public void setLocation(Point p) {
+    points = null;
+    bounds = null;
+    location.setLocation(p);
+    transform.setTranslation(p.x, p.y);
+}
+
+/**
+ * Sets the PolylineDecoration's point template_. This template_ is an outline of the
+ * PolylineDecoration's region. (The default value is TRIANGLE_TIP which is a triangle
+ * whose tip is pointing to the right).
+ *
+ * @param pl the template_
+ * @since 2.0
+ */
+public void setTemplate(PointList pl) {
+    erase();
+    template_ = pl;
+    points = null;
+    bounds = null;
+    repaint();
+}
+
+/**
+ * Sets the amount of scaling to be done along X and Y axes on the PolylineDecoration's
+ * template_.
+ *
+ * @param x the x scale
+ * @param y the y scale
+ * @since 2.0
+ */
+public void setScale(double x, double y) {
+    points = null;
+    bounds = null;
+    transform.setScale(x, y);
+}
+
+/**
+ * @see RotatableDecoration#setReferencePoint(Point)
+ */
+public void setReferencePoint(Point ref_) {
+    Point pt = Point.SINGLETON;
+    pt.setLocation(ref_);
+    pt.negate().translate(location);
+    setRotation(Math.atan2(pt.y, pt.x));
+}
+
+/**
+ * Sets the angle by which rotation is to be done on the PolylineDecoration.
+ *
+ * @param angle the angle of rotation
+ * @since 2.0
+ */
+public void setRotation(double angle) {
+    points = null;
+    bounds = null;
+    transform.setRotation(angle);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/PopUpHelper.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,216 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.PopUpHelper;
+
+import dwt.dwthelper.utils;
+
+
+
+import dwt.DWT;
+import dwt.graphics.Color;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Control;
+import dwt.widgets.Shell;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.LightweightSystem;
+
+/**
+ * Provides abstract support for classes that manage popups. Popups in Draw2d consist of a
+ * LightweightSystem object with an DWT shell as its Control. Desired popup behavior is
+ * attained by adding appropriate listeners to this shell.
+ */
+public abstract class PopUpHelper {
+
+private Shell shell;
+private LightweightSystem lws;
+private bool tipShowing;
+/**
+ * The Control this PopUpHelper's tooltip will belong to.
+ */
+protected Control control;
+
+/**
+ * These style bits should be used when creating the Shell.
+ * @see #createShell()
+ */
+protected const int shellStyle;
+
+/**
+ * Constructs a PopUpHelper to assist with popups on Control c.
+ *
+ * @param c the Control
+ * @since 2.0
+ */
+protected this(Control c) {
+    this (c, DWT.ON_TOP | DWT.NO_TRIM);
+}
+
+/**
+ * Constructs a PopUpHelper to display the given shell style popup.
+ * @param c the control on which the popup is active.
+ * @param shellStyle the DWT style bits for the shell
+ * @since 3.1
+ */
+protected this(Control c, int shellStyle) {
+    control = c;
+    this.shellStyle = shellStyle | DWT.NO_BACKGROUND | DWT.NO_REDRAW_RESIZE;
+}
+
+/**
+ * Creates and returns the LightweightSystem object used by PopUpHelper to draw upon.
+ *
+ * @return the newly created LightweightSystem
+ * @since 2.0
+ */
+protected LightweightSystem createLightweightSystem() {
+    return new LightweightSystem();
+}
+
+/**
+ * Creates a new Shell object with the style specified for this helper.
+ *
+ * @return the newly created Shell
+ * @since 2.0
+ */
+protected Shell createShell() {
+    return new Shell(control.getShell(), shellStyle);
+}
+
+/**
+ * Dispose of this PopUpHelper object.
+ *
+ * @since 2.0
+ */
+public void dispose() {
+    if (isShowing())
+        hide();
+    if (shell !is null && !shell.isDisposed())
+        shell.dispose();
+}
+
+/**
+ * Returns this PopUpHelper's shell. If no shell exists for this PopUpHelper, a new shell
+ * is created and hookShellListeners() is called.
+ *
+ * @return the Shell
+ * @since 2.0
+ */
+protected Shell getShell() {
+    if (shell is null) {
+        shell = createShell();
+        hookShellListeners();
+    }
+    return shell;
+}
+
+/**
+ * Returns the size needed to display the shell's trim.  This method should not be called
+ * until the shell has been created.
+ * @return the size of the shells trim.
+ * @since 3.1
+ */
+protected Dimension getShellTrimSize() {
+    Rectangle trim = shell.computeTrim(0, 0, 0, 0);
+    return new Dimension(trim.width, trim.height);
+}
+
+/**
+ * Returns this PopUpHelper's LightweightSystem. If no LightweightSystem exists for this
+ * PopUpHelper, a new LightweightSystem is created with this PopUpHelper's Shell as its
+ * Control.
+ *
+ * @return the LightweightSystem
+ * @since 2.0
+ */
+protected LightweightSystem getLightweightSystem() {
+    if (lws is null) {
+        lws = createLightweightSystem();
+        lws.setControl(getShell());
+    }
+    return lws;
+}
+
+/**
+ * Hides this PopUpHelper's Shell.
+ *
+ * @since 2.0
+ */
+protected void hide() {
+    if (shell !is null && !shell.isDisposed())
+        shell.setVisible(false);
+    tipShowing = false;
+}
+
+/**
+ * Desired popup helper behavior is achieved by writing listeners that manipulate the
+ * behavior of the PopUpHelper's Shell. Override this method and add these listeners here.
+ *
+ * @since 2.0
+ */
+protected abstract void hookShellListeners();
+
+/**
+ * Returns <code>true</code> if this PopUpHelper's Shell is visible, <code>false</code>
+ * otherwise.
+ *
+ * @return <code>true</code> if this PopUpHelper's Shell is visible
+ * @since 2.0
+ */
+public bool isShowing() {
+    return tipShowing;
+}
+
+/**
+ * Sets the background color of this PopUpHelper's Shell.
+ *
+ * @param c the new background color
+ * @since 2.0
+ */
+public void setBackgroundColor(Color c) {
+    getShell().setBackground(c);
+}
+
+/**
+ * Sets the foreground color of this PopUpHelper's Shell.
+ *
+ * @param c the new foreground color
+ * @since 2.0
+ */
+public void setForegroundColor(Color c) {
+    getShell().setForeground(c);
+}
+
+/**
+ * Sets the bounds on this PopUpHelper's Shell.
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param width the width
+ * @param height the height
+ * @since 2.0
+ */
+protected void setShellBounds(int x, int y, int width, int height) {
+    getShell().setBounds(x, y, width, height);
+}
+
+/**
+ * Displays this PopUpHelper's Shell.
+ *
+ * @since 2.0
+ */
+protected void show() {
+    getShell().setVisible(true);
+    tipShowing = true;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/PositionConstants.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.PositionConstants;
+
+import dwt.dwthelper.utils;
+
+/**
+ * Constants representing cardinal directions and relative positions. Some of these
+ * constants can be grouped as follows:
+ * <TABLE border="1" cellpadding="5" cellspacing="0">
+ *  <TBODY>
+ *      <TR>
+ *          <TD>LEFT, CENTER, RIGHT</TD>
+ *          <TD>Used to describe horizontal position.</TD>
+ *      </TR>
+ *      <TR>
+ *          <TD>TOP, MIDDLE, BOTTOM</TD>
+ *          <TD>Used to describe vertical position.</TD>
+ *      </TR>
+ *      <TR>
+ *          <TD>NORTH, SOUTH, EAST, WEST</TD>
+ *          <TD>Used to describe the four positions relative to an object's center point.
+ *          May also be used when describing which direction an object is facing.<BR>
+ *          NOTE: If you have a use for all four of these possibilities, do not use TOP,
+ *          BOTTOM, RIGHT, LEFT in place of NORTH, SOUTH, EAST, WEST.</TD>
+ *      </TR>
+ *  </TBODY>
+ * </TABLE>
+ */
+public interface PositionConstants {
+
+/** None */
+static const int NONE  =  0;
+
+/** Left */
+static const int LEFT   =  1;
+/** Center (Horizontal) */
+static const int CENTER =  2;
+/** Right */
+static const int RIGHT  =  4;
+/** Bit-wise OR of LEFT, CENTER, and RIGHT */
+static const int LEFT_CENTER_RIGHT = LEFT | CENTER | RIGHT;
+/** Used to signify left alignment regardless of orientation (i.e., LTR or RTL) */
+static const int ALWAYS_LEFT = 64;
+/** Used to signify right alignment regardless of orientation (i.e., LTR or RTL) */
+static const int ALWAYS_RIGHT = 128;
+
+/** Top */
+static const int TOP    =  8;
+/** Middle (Vertical) */
+static const int MIDDLE = 16;
+/** Bottom */
+static const int BOTTOM = 32;
+/** Bit-wise OR of TOP, MIDDLE, and BOTTOM */
+static const int TOP_MIDDLE_BOTTOM = TOP | MIDDLE | BOTTOM;
+
+/** North */
+static const int NORTH =  1;
+/** South */
+static const int SOUTH =  4;
+/** West */
+static const int WEST  =  8;
+/** East */
+static const int EAST  = 16;
+
+/** A constant indicating horizontal direction */
+static const int HORIZONTAL = 64;
+/** A constant indicating vertical direction */
+static const int VERTICAL = 128;
+
+/** North-East: a bit-wise OR of {@link #NORTH} and {@link #EAST} */
+static const int NORTH_EAST = NORTH | EAST;
+/** North-West: a bit-wise OR of {@link #NORTH} and {@link #WEST} */
+static const int NORTH_WEST = NORTH | WEST;
+/** South-East: a bit-wise OR of {@link #SOUTH} and {@link #EAST} */
+static const int SOUTH_EAST = SOUTH | EAST;
+/** South-West: a bit-wise OR of {@link #SOUTH} and {@link #WEST} */
+static const int SOUTH_WEST = SOUTH | WEST;
+/** North-South: a bit-wise OR of {@link #NORTH} and {@link #SOUTH} */
+static const int NORTH_SOUTH = NORTH | SOUTH;
+/** East-West: a bit-wise OR of {@link #EAST} and {@link #WEST} */
+static const int EAST_WEST = EAST | WEST;
+
+/** North-South-East-West: a bit-wise OR of all 4 directions. */
+static const int NSEW = NORTH_SOUTH | EAST_WEST;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/PrintFigureOperation.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,208 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ *     Sven M�ller - Added tiling support
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.PrintFigureOperation;
+
+import dwt.dwthelper.utils;
+
+
+
+import dwt.DWT;
+import dwt.graphics.Color;
+import dwt.printing.Printer;
+import dwt.widgets.Display;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.PrintOperation;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.ColorConstants;
+
+/**
+ * Class responsible for printing Figures.
+ *
+ * @author Dan Lee
+ * @author Eric Bordeau
+ * @author Sven M�ller
+ */
+public class PrintFigureOperation : PrintOperation {
+
+/**
+ * The default print mode. Prints at 100% scale and tiles horizontally and/or vertically,
+ * if necessary.
+ */
+public static const int TILE = 1;
+/**
+ * A print mode that scales the printer graphics so that the entire printed image fits on
+ * one page.
+ */
+public static const int FIT_PAGE = 2;
+/**
+ * A print mode that scales the printer graphics so that the width of the printed image
+ * fits on one page and tiles vertically, if necessary.
+ */
+public static const int FIT_WIDTH = 3;
+/**
+ * A print mode that scales the printer graphics so that the height of the printed image
+ * fits on one page and tiles horizontally, if necessary.
+ */
+public static const int FIT_HEIGHT = 4;
+
+private IFigure printSource;
+private Color oldBGColor;
+private int printMode = TILE;
+
+/**
+ * Constructor for PrintFigureOperation.
+ * <p>
+ * Note: Descendants must call setPrintSource(IFigure) to set the IFigure that is to be
+ * printed.
+ * @see dwtx.draw2d.PrintOperation#PrintOperation(Printer)
+ */
+protected this(Printer p) {
+    super(p);
+}
+
+/**
+ * Constructor for PrintFigureOperation.
+ *
+ * @param p Printer to print on
+ * @param srcFigure Figure to print
+ */
+public this(Printer p, IFigure srcFigure) {
+    super(p);
+    setPrintSource(srcFigure);
+}
+
+/**
+ * @return DWT.RIGHT_TO_LEFT if the print source is mirrored; DWT.LEFT_TO_RIGHT otherwise
+ * @see dwtx.draw2d.PrintOperation#getGraphicsOrientation()
+ */
+int getGraphicsOrientation() {
+    return getPrintSource().isMirrored() ? DWT.RIGHT_TO_LEFT : DWT.LEFT_TO_RIGHT;
+}
+
+/**
+ * Returns the current print mode.  The print mode is one of: {@link #FIT_HEIGHT},
+ * {@link #FIT_PAGE}, or {@link #FIT_WIDTH}.
+ * @return the print mode
+ */
+protected int getPrintMode() {
+    return printMode;
+}
+
+/**
+ * Returns the printSource.
+ *
+ * @return IFigure The source IFigure
+ */
+protected IFigure getPrintSource() {
+    return printSource;
+}
+
+/**
+ * @see dwtx.draw2d.PrintOperation#preparePrintSource()
+ */
+protected void preparePrintSource() {
+    oldBGColor = getPrintSource().getLocalBackgroundColor();
+    getPrintSource().setBackgroundColor(ColorConstants.white);
+}
+
+/**
+ * Prints the pages based on the current print mode.
+ * @see dwtx.draw2d.PrintOperation#printPages()
+ */
+protected void printPages() {
+    Graphics graphics = getFreshPrinterGraphics();
+    IFigure figure = getPrintSource();
+    setupPrinterGraphicsFor(graphics, figure);
+    Rectangle bounds = figure.getBounds();
+    int x = bounds.x, y = bounds.y;
+    Rectangle clipRect = new Rectangle();
+    while (y < bounds.y + bounds.height) {
+        while (x < bounds.x + bounds.width) {
+            graphics.pushState();
+            getPrinter().startPage();
+            graphics.translate(-x, -y);
+            graphics.getClip(clipRect);
+            clipRect.setLocation(x, y);
+            graphics.clipRect(clipRect);
+            figure.paint(graphics);
+            getPrinter().endPage();
+            graphics.popState();
+            x += clipRect.width;
+        }
+        x = bounds.x;
+        y += clipRect.height;
+    }
+}
+
+/**
+ * @see dwtx.draw2d.PrintOperation#restorePrintSource()
+ */
+protected void restorePrintSource() {
+    getPrintSource().setBackgroundColor(oldBGColor);
+    oldBGColor = null;
+}
+
+/**
+ * Sets the print mode.  Possible values are {@link #TILE}, {@link #FIT_HEIGHT},
+ * {@link #FIT_WIDTH} and {@link #FIT_PAGE}.
+ * @param mode the print mode
+ */
+public void setPrintMode(int mode) {
+    printMode = mode;
+}
+
+/**
+ * Sets the printSource.
+ * @param printSource The printSource to set
+ */
+protected void setPrintSource(IFigure printSource) {
+    this.printSource = printSource;
+}
+
+/**
+ * Sets up Graphics object for the given IFigure.
+ * @param graphics The Graphics to setup
+ * @param figure The IFigure used to setup graphics
+ */
+protected void setupPrinterGraphicsFor(Graphics graphics, IFigure figure) {
+    double dpiScale = cast(double)getPrinter().getDPI().x / Display.getCurrent().getDPI().x;
+
+    Rectangle printRegion = getPrintRegion();
+    // put the print region in display coordinates
+    printRegion.width /= dpiScale;
+    printRegion.height /= dpiScale;
+
+    Rectangle bounds = figure.getBounds();
+    double xScale = cast(double)printRegion.width / bounds.width;
+    double yScale = cast(double)printRegion.height / bounds.height;
+    switch (getPrintMode()) {
+        case FIT_PAGE:
+            graphics.scale(Math.min(xScale, yScale) * dpiScale);
+            break;
+        case FIT_WIDTH:
+            graphics.scale(xScale * dpiScale);
+            break;
+        case FIT_HEIGHT:
+            graphics.scale(yScale * dpiScale);
+            break;
+        default:
+            graphics.scale(dpiScale);
+    }
+    graphics.setForegroundColor(figure.getForegroundColor());
+    graphics.setBackgroundColor(figure.getBackgroundColor());
+    graphics.setFont(figure.getFont());
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/PrintOperation.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.PrintOperation;
+
+import dwt.dwthelper.utils;
+
+static import dwt.graphics.Rectangle;
+static import dwt.graphics.Point;
+
+import dwt.DWT;
+import dwt.graphics.GC;
+import dwt.printing.Printer;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.PrinterGraphics;
+import dwtx.draw2d.SWTGraphics;
+
+
+/**
+ * Implementation of draw2d's printing capabilities.
+ *
+ * @author Dan Lee
+ * @author Eric Bordeau
+ */
+public abstract class PrintOperation {
+
+private GC printerGC;  // Note: Only one GC instance should be created per print job
+private Insets printMargin;
+private Printer printer;
+private PrinterGraphics printerGraphics;
+private SWTGraphics g;
+
+/**
+ * Creates a new PrintOperation
+ */
+public this() {
+    printMargin = new Insets(0, 0, 0, 0);
+}
+
+/**
+ * Creates a new PrintOperation on Printer p
+ * @param p The printer to print on
+ */
+public this(Printer p) {
+    this();
+    setPrinter(p);
+}
+
+/**
+ * Disposes the PrinterGraphics and GC objects associated with this PrintOperation.
+ */
+protected void cleanup() {
+    if (g !is null) {
+        printerGraphics.dispose();
+        g.dispose();
+    }
+    if (printerGC !is null)
+        printerGC.dispose();
+}
+
+/**
+ * Returns a new PrinterGraphics setup for the Printer associated with this
+ * PrintOperation.
+ *
+ * @return PrinterGraphics The new PrinterGraphics
+ */
+protected PrinterGraphics getFreshPrinterGraphics() {
+    if (printerGraphics !is null) {
+        printerGraphics.dispose();
+        g.dispose();
+        printerGraphics = null;
+        g = null;
+    }
+    g = new SWTGraphics(printerGC);
+    printerGraphics = new PrinterGraphics(g, printer);
+    setupGraphicsForPage(printerGraphics);
+    return printerGraphics;
+}
+
+/**
+ * This method is invoked by the {@link #run(String)} method to determine the orientation
+ * of the GC to be used for printing.  This default implementation always returns
+ * DWT.LEFT_TO_RIGHT.
+ * @return DWT.LEFT_TO_RIGHT or DWT.RIGHT_TO_LEFT
+ * @since 3.1
+ * @TODO Make protected post-3.1
+ */
+int getGraphicsOrientation() {
+    return DWT.LEFT_TO_RIGHT;
+}
+
+/**
+ * Returns the printer.
+ * @return Printer
+ */
+public Printer getPrinter() {
+    return printer;
+}
+
+/**
+ * Returns a Rectangle that represents the region that can be printed to. The x, y,
+ * height, and width values are using the printers coordinates.
+ * @return the print region
+ */
+public Rectangle getPrintRegion() {
+    dwt.graphics.Rectangle.Rectangle trim = printer.computeTrim(0, 0, 0, 0);
+    dwt.graphics.Point.Point printerDPI = printer.getDPI();
+    Insets notAvailable = new Insets(-trim.y, -trim.x,
+            trim.height + trim.y, trim.width + trim.x);
+    Insets userPreferred = new Insets(
+            (printMargin.top * printerDPI.x) / 72,
+            (printMargin.left * printerDPI.x) / 72,
+            (printMargin.bottom * printerDPI.x) / 72,
+            (printMargin.right * printerDPI.x) / 72);
+    Rectangle paperBounds = new Rectangle(printer.getBounds());
+    Rectangle printRegion = paperBounds.getCropped(notAvailable);
+    printRegion.intersect(paperBounds.getCropped(userPreferred));
+    printRegion.translate(trim.x, trim.y);
+    return printRegion;
+}
+
+/**
+ * This method contains all operations performed to sourceFigure prior to being printed.
+ */
+protected void preparePrintSource() { }
+
+/**
+ * This method is responsible for printing pages. (A page is printed by calling
+ * Printer.startPage(), followed by painting to the PrinterGraphics object, and then
+ * calling Printer.endPage()).
+ */
+protected abstract void printPages();
+
+/**
+ * This method contains all operations performed to
+ * sourceFigure after being printed.
+ */
+protected void restorePrintSource() { }
+
+/**
+ * Sets the print job into motion.
+ *
+ * @param jobName A String representing the name of the print job
+ */
+public void run(String jobName) {
+    preparePrintSource();
+    if (printer.startJob(jobName)) {
+        printerGC = new GC(getPrinter(), getGraphicsOrientation());
+        printPages();
+        printer.endJob();
+    }
+    restorePrintSource();
+    cleanup();
+}
+
+/**
+ * Sets the printer.
+ * @param printer The printer to set
+ */
+public void setPrinter(Printer printer) {
+    this.printer = printer;
+}
+
+/**
+ * Sets the page margin in pels (logical pixels) to the passed Insets.(72 pels is 1 inch)
+ *
+ * @param margin The margin to set on the page
+ */
+public void setPrintMargin(Insets margin) {
+    printMargin = margin;
+}
+
+/**
+ * Manipulates the PrinterGraphics to position it to paint in the desired region of the
+ * page. (Default is the top left corner of the page).
+ *
+ * @param pg The PrinterGraphics to setup
+ */
+protected void setupGraphicsForPage(PrinterGraphics pg) {
+    Rectangle printRegion = getPrintRegion();
+    pg.clipRect(printRegion);
+    pg.translate(printRegion.getTopLeft());
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/PrinterGraphics.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.PrinterGraphics;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwt.graphics.Font;
+import dwt.graphics.FontData;
+import dwt.graphics.Image;
+import dwt.printing.Printer;
+import dwt.widgets.Display;
+import dwtx.draw2d.ScaledGraphics;
+import dwtx.draw2d.Graphics;
+
+/**
+ * A scalable graphics object used to print to a printer.
+ * @author danlee
+ */
+public class PrinterGraphics : ScaledGraphics {
+
+Map imageCache;
+
+Printer printer;
+
+/**
+ * Creates a new PrinterGraphics with Graphics g, using Printer p
+ * @param g Graphics object to draw with
+ * @param p Printer to print to
+ */
+public this(Graphics g, Printer p) {
+    imageCache = new HashMap();
+    super(g);
+    printer = p;
+}
+
+Font createFont(FontData data) {
+    return new Font(printer, data);
+}
+
+private Image printerImage(Image image) {
+    Image result = cast(Image)imageCache.get(image);
+    if (result !is null)
+        return result;
+
+    result = new Image(printer, image.getImageData());
+    imageCache.put(image, result);
+    return result;
+}
+
+/**
+ * @see dwtx.draw2d.ScaledGraphics#dispose()
+ */
+public void dispose() {
+    super.dispose();
+
+    //Dispose printer images
+    Iterator iter = imageCache.values().iterator();
+    while (iter.hasNext()) {
+        Image printerImage = (cast(Image)iter.next());
+        printerImage.dispose();
+    }
+
+    imageCache.clear();
+}
+
+/**
+ * @see dwtx.draw2d.Graphics#drawImage(Image, int, int)
+ */
+public void drawImage(Image srcImage, int x, int y) {
+    super.drawImage(printerImage(srcImage), x, y);
+}
+
+/**
+ * @see Graphics#drawImage(Image, int, int, int, int, int, int, int, int)
+ */
+public void drawImage(Image srcImage, int sx, int sy, int sw, int sh,
+                                        int tx, int ty, int tw, int th) {
+    super.drawImage(printerImage(srcImage), sx, sy, sw, sh, tx, ty, tw, th);
+}
+
+int zoomFontHeight(int height) {
+    return cast(int)
+        (height * zoom * Display.getCurrent().getDPI().y / printer.getDPI().y
+            + 0.0000001);
+}
+
+int zoomLineWidth(int w) {
+    return cast(int)(w * zoom);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/RangeModel.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.RangeModel;
+
+import dwt.dwthelper.utils;
+
+import dwtx.dwtxhelper.Bean;
+
+/**
+ * This interface represents a range of possible values as well as the current values.
+ * There is a minumum and maximum value, a current value, and the extent.  One use for a
+ * RangeModel is a scrollbar.  There is a minimum value (the top of the scrollbar), a
+ * maximum value (the bottom of the scrollbar), a current value (the top of the thumb),
+ * and an extent (the length of the thumb).
+ */
+public interface RangeModel {
+
+/** Value property name */
+static const String PROPERTY_VALUE = "value"; //$NON-NLS-1$
+/** Extent property name */
+static const String PROPERTY_EXTENT = "extent"; //$NON-NLS-1$
+/** Minimum property name */
+static const String PROPERTY_MINIMUM = "minimum"; //$NON-NLS-1$
+/** Maximum property name */
+static const String PROPERTY_MAXIMUM = "maximum"; //$NON-NLS-1$
+
+/**
+ * Registers listener as a PropertyChangeListener of this RangeModel.  Listeners will be
+ * notified of changes to {@link #PROPERTY_VALUE value}, {@link #PROPERTY_EXTENT extent},
+ * {@link #PROPERTY_MINIMUM minimum} and {@link #PROPERTY_MAXIMUM maximum} properties.
+ *
+ * @param listener The listener to add
+ */
+void addPropertyChangeListener(PropertyChangeListener listener);
+
+/**
+ * Returns the extent.
+ * @return The extent
+ */
+int getExtent();
+
+/**
+ * Returns the maximum value in the range.
+ * @return The maximum value
+ */
+int getMaximum();
+
+/**
+ * Returns the minimum value in the range.
+ * @return The minimum value
+ */
+int getMinimum();
+
+/**
+ * Returns the current value.
+ * @return The current value
+ */
+int getValue();
+
+/**
+ * Returns <code>true</code> if this RangeModel is enabled.
+ * @return <code>true</code> if this Rangel Model is enabled
+ */
+bool isEnabled();
+
+/**
+ * Removes the given listener from this RangeModel's list of PropertyChangeListeners.
+ * @param listener The listener to remove
+ */
+void removePropertyChangeListener(PropertyChangeListener listener);
+
+/**
+ * Sets min, extent, and max all at once.
+ * @param min the new mininum
+ * @param extent the new extent
+ * @param max the new maximum
+ */
+void setAll(int min, int extent, int max);
+
+/**
+ * Sets the extent.
+ * @param extent The extent
+ */
+void setExtent(int extent);
+
+/**
+ * Sets the maximum value of the range.
+ * @param max The maximum value
+ */
+void setMaximum(int max);
+
+/**
+ * Sets the minimum value of the range.
+ * @param min The minimum value
+ */
+void setMinimum(int min);
+
+/**
+ * Sets the current value.
+ * @param value The current value
+ */
+void setValue(int value);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/RectangleFigure.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.RectangleFigure;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.Shape;
+import dwtx.draw2d.Graphics;
+
+/**
+ * Draws a rectangle whose size is determined by the bounds set to it.
+ */
+public class RectangleFigure : Shape {
+
+/**
+ * Creates a RectangleFigure.
+ */
+public this() { }
+
+/**
+ * @see Shape#fillShape(Graphics)
+ */
+protected void fillShape(Graphics graphics) {
+    graphics.fillRectangle(getBounds());
+}
+
+/**
+ * @see Shape#outlineShape(Graphics)
+ */
+protected void outlineShape(Graphics graphics) {
+    Rectangle r = getBounds();
+    int x = r.x + lineWidth / 2;
+    int y = r.y + lineWidth / 2;
+    int w = r.width - Math.max(1, lineWidth);
+    int h = r.height - Math.max(1, lineWidth);
+    graphics.drawRectangle(x, y, w, h);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/RelativeBendpoint.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.RelativeBendpoint;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PrecisionPoint;
+import dwtx.draw2d.Bendpoint;
+import dwtx.draw2d.Connection;
+
+/**
+ * RelativeBendpoint is a Bendpoint that calculates its location based on its distance
+ * from the start and end points of the {@link Connection}, as well as its weight. See
+ * {@link #setWeight(float)} for a description of what behavior different weights will
+ * provide.
+ */
+public class RelativeBendpoint
+    : Bendpoint
+{
+
+private Connection connection;
+private float weight = 0.5f;
+private Dimension d1, d2;
+
+/**
+ * Constructs a new RelativeBendpoint.
+ *
+ * @since 2.0
+ */
+public this() { }
+
+/**
+ * Constructs a new RelativeBendpoint and associates it with the given Connection.
+ * @param conn The Connection this Bendpoint is associated with
+ * @since 2.0
+ */
+public this(Connection conn) {
+    setConnection(conn);
+}
+
+/**
+ * Returns the Connection this Bendpoint is associated with.
+ * @return The Connection this Bendpoint is associated with
+ * @since 2.0
+ */
+protected Connection getConnection() {
+    return connection;
+}
+
+/**
+ * Calculates and returns this Bendpoint's new location.
+ * @return This Bendpoint's new location
+ * @since 2.0
+ */
+public Point getLocation() {
+    PrecisionPoint a1 = new PrecisionPoint(getConnection().getSourceAnchor().getReferencePoint());
+    PrecisionPoint a2 = new PrecisionPoint(getConnection().getTargetAnchor().getReferencePoint());
+
+    getConnection().translateToRelative(a1);
+    getConnection().translateToRelative(a2);
+
+    return new PrecisionPoint((a1.preciseX() + d1.preciseWidth())
+                * (1f - weight) + weight * (a2.preciseX() + d2.preciseWidth()),
+                (a1.preciseY() + d1.preciseHeight()) * (1f - weight) + weight
+                        * (a2.preciseY() + d2.preciseHeight()));
+    }
+
+/**
+ * Sets the Connection this bendpoint should be associated with.
+ * @param conn The Connection this bendpoint should be associated with
+ * @since 2.0
+ */
+public void setConnection(Connection conn) {
+    connection = conn;
+}
+
+/**
+ * Sets the Dimensions representing the X and Y distances this Bendpoint is from the start
+ * and end points of the Connection. These Dimensions are generally set once and are used
+ * in  calculating the Bendpoint's location.
+ * @param dim1 The X and Y distances this Bendpoint is from the start of the Connection
+ * @param dim2 The X and Y distances this Bendpoint is from the end of the Connection
+ * @since 2.0
+ */
+public void setRelativeDimensions(Dimension dim1, Dimension dim2) {
+    d1 = dim1;
+    d2 = dim2;
+}
+
+/**
+ * Sets the weight this Bendpoint should use to calculate its location. The weight should
+ * be between 0.0 and 1.0. A weight of 0.0 will cause the Bendpoint to follow the start
+ * point, while a weight of 1.0 will cause the Bendpoint to follow the end point. A weight
+ * of 0.5 (the default) will cause the Bendpoint to maintain its original aspect ratio
+ * between the start and end points.
+ * @param w The weight
+ * @since 2.0
+ */
+public void setWeight(float w) {
+    weight = w;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/RelativeLocator.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.RelativeLocator;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.PrecisionRectangle;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.Locator;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.PositionConstants;
+
+/**
+ * Places a handle relative to a figure's bounds. The placement is determined by
+ * indicating the figure to which the placement is relative, and two floating-point value
+ * indicating the horizontal and vertical offset from that figure's top-left corner.  The
+ * values (0.0, 0.0) would indicate the figure's top-left corner, while the values (1.0,
+ * 1.0) would indicate the figure's bottom-right corner.
+ * <P>
+ * Constants such as {@link PositionConstants#NORTH NORTH} and {@link
+ * PositionConstants#SOUTH SOUTH} can be used to set the placement.
+ */
+public class RelativeLocator
+    : Locator
+{
+
+private double  relativeX;
+private double  relativeY;
+private IFigure reference;
+
+/**
+ * Null constructor. The reference figure must be set before use.  The relative locations
+ * will default to (0.0, 0.0).
+ * @since 2.0
+ */
+public this() {
+    relativeX = 0.0;
+    relativeY = 0.0;
+}
+
+/**
+ * Constructs a RelativeLocator with the given reference figure and relative location. The
+ * location is a constant from {@link PositionConstants} used as a convenient and readable
+ * way to set both the relativeX and relativeY values.
+ * @param reference the reference figure
+ * @param location one of NORTH, NORTH_EAST, etc.
+ * @since 2.0
+ */
+public this(IFigure reference, int location) {
+    setReferenceFigure(reference);
+    switch (location & PositionConstants.NORTH_SOUTH) {
+        case PositionConstants.NORTH:
+            relativeY = 0; break;
+        case PositionConstants.SOUTH:
+            relativeY = 1.0; break;
+        default:
+            relativeY = 0.5;
+    }
+
+    switch (location & PositionConstants.EAST_WEST) {
+        case PositionConstants.WEST:
+            relativeX = 0; break;
+        case PositionConstants.EAST:
+            relativeX = 1.0; break;
+        default:
+            relativeX = 0.5;
+    }
+}
+
+/**
+ * Constructs a RelativeLocator with the given reference Figure and offset ratios.
+ * @param reference the reference figure
+ * @param relativeX the relative X offset
+ * @param relativeY the relative Y offset
+ * @since 2.0
+ */
+public this(IFigure reference, double relativeX, double relativeY) {
+    setReferenceFigure(reference);
+    this.relativeX = relativeX;
+    this.relativeY = relativeY;
+}
+
+/**
+ * Returns the Reference Box in the Reference Figure's coordinate system.
+ * The returned Rectangle may be by reference, and should <b>not</b> be modified.
+ * @return the reference box
+ * @since 2.0
+ */
+protected Rectangle getReferenceBox() {
+    return getReferenceFigure().getBounds();
+}
+
+/**
+ * Returns the Figure this locator is relative to.
+ * @return the reference figure
+ * @since 2.0
+ */
+protected IFigure getReferenceFigure() {
+    return reference;
+}
+
+/**
+ * Relocates the target using the relative offset locations.
+ * @see dwtx.draw2d.Locator#relocate(dwtx.draw2d.IFigure)
+ */
+public void relocate(IFigure target) {
+    IFigure reference = getReferenceFigure();
+    Rectangle targetBounds = new PrecisionRectangle(getReferenceBox().getResized(-1, -1));
+    reference.translateToAbsolute(targetBounds);
+    target.translateToRelative(targetBounds);
+    targetBounds.resize(1, 1);
+
+    Dimension targetSize = target.getPreferredSize();
+
+    targetBounds.x
+        += cast(int) (targetBounds.width * relativeX - ((targetSize.width + 1) / 2));
+    targetBounds.y
+        += cast(int) (targetBounds.height * relativeY - ((targetSize.height + 1) / 2));
+    targetBounds.setSize(targetSize);
+    target.setBounds(targetBounds);
+}
+
+/**
+ * Sets the reference figure this locator uses to place the target figure.
+ * @param reference the reference figure
+ * @since 2.0
+ */
+public void setReferenceFigure(IFigure reference) {
+    this.reference = reference;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/RotatableDecoration.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.RotatableDecoration;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.IFigure;
+
+/**
+ * An IFigure that can be rotated.
+ */
+public interface RotatableDecoration
+    : IFigure
+{
+
+/**
+ * Sets the location of this figure.
+ * @param p The location
+ */
+void setLocation(Point p);
+
+/**
+ * Sets the reference point used to determine the rotation angle.
+ * @param p The reference point
+ */
+void setReferencePoint(Point p);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/RoundedRectangle.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.RoundedRectangle;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.Shape;
+import dwtx.draw2d.Graphics;
+
+/**
+ * Draws a Rectangle whose corners are rounded in appearance. The size of the rectangle is
+ * determined by the bounds set to it.
+ */
+public class RoundedRectangle : Shape {
+
+/** The width and height radii applied to each corner. */
+protected Dimension corner;
+
+/**
+ * Constructs a round cornered rectangle.
+ */
+public this() {
+    corner = new Dimension(8, 8);
+}
+
+/**
+ * @see Shape#fillShape(Graphics)
+ */
+protected void fillShape(Graphics graphics) {
+    graphics.fillRoundRectangle(getBounds(), corner.width, corner.height);
+}
+
+/**
+ * @see Shape#outlineShape(Graphics)
+ */
+protected void outlineShape(Graphics graphics) {
+    Rectangle f = Rectangle.SINGLETON;
+    Rectangle r = getBounds();
+    f.x = r.x + lineWidth / 2;
+    f.y = r.y + lineWidth / 2;
+    f.width = r.width - lineWidth;
+    f.height = r.height - lineWidth;
+    graphics.drawRoundRectangle(f, corner.width, corner.height);
+}
+
+/**
+ * Sets the dimensions of each corner. This will form the radii of the arcs which form the
+ * corners.
+ *
+ * @param d the dimensions of the corner
+ * @since 2.0
+ */
+public void setCornerDimensions(Dimension d) {
+    corner.width = d.width;
+    corner.height = d.height;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/RoutingAnimator.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,216 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.RoutingAnimator;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.Animator;
+import dwtx.draw2d.RoutingListener;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Connection;
+import dwtx.draw2d.Animation;
+
+/**
+ * Animates the routing of a connection. The animator will capture the effects of the
+ * connection's router, and the play back the placement of the routing, interpolating the
+ * intermediate routes.
+ * <P>
+ * To use a routing animator, hook it as a routing listener for the connection whose
+ * points are to be animated, by calling {@link
+ * PolylineConnection#addRoutingListener(RoutingListener)}. An animator is active
+ * only when the Animation utility is activated.
+ *
+ * @since 3.2
+ */
+public class RoutingAnimator : Animator , RoutingListener {
+
+static const RoutingAnimator INSTANCE;
+static this(){
+    INSTANCE = new RoutingAnimator();
+}
+/**
+ * Constructs a routing animator for use with one or more connections. The default
+ * instance ({@link #getDefault()} can be used on any number of connections.
+ *
+ * @since 3.2
+ */
+protected this() { }
+
+/**
+ * Overridden to sync initial and final states.
+ * @see Animator#playbackStarting(IFigure)
+ */
+public void playbackStarting(IFigure connection) {
+    reconcileStates(cast(Connection)connection);
+}
+
+/**
+ * Returns the current state of the connection. Currently, this is a copy of the list of
+ * points. However this Object could change in future releases and should not be
+ * considered API.
+ * @see Animator#getCurrentState(IFigure)
+ */
+protected Object getCurrentState(IFigure connection) {
+    return (cast(Connection)connection).getPoints().getCopy();
+}
+
+/**
+ * Returns the default instance.
+ * @return the default instance
+ * @since 3.2
+ */
+public static RoutingAnimator getDefault() {
+    return INSTANCE;
+}
+
+/**
+ * Hooks invalidate for animation purposes.
+ * @see RoutingListener#invalidate(Connection)
+ */
+public final void invalidate(Connection conn) {
+    if (Animation.isInitialRecording())
+        Animation.hookAnimator(conn, this);
+}
+
+/**
+ * Plays back the interpolated state.
+ * @see Animator#playback(IFigure)
+ */
+protected bool playback(IFigure figure) {
+    Connection conn = cast(Connection) figure;
+
+    PointList list1 = cast(PointList)Animation.getInitialState(this, conn);
+    PointList list2 = cast(PointList)Animation.getFinalState(this, conn);
+    if (list1 is null) {
+        conn.setVisible(false);
+        return true;
+    }
+
+    float progress = Animation.getProgress();
+    if (list1.size() is list2.size()) {
+        Point pt1 = new Point(), pt2 = new Point();
+        PointList points = conn.getPoints();
+        points.removeAllPoints();
+        for (int i = 0; i < list1.size(); i++) {
+            list1.getPoint(pt2, i);
+            list2.getPoint(pt1, i);
+            pt1.x = cast(int)Math.round(pt1.x * progress + (1 - progress) * pt2.x);
+            pt1.y = cast(int)Math.round(pt1.y * progress + (1 - progress) * pt2.y);
+            points.addPoint(pt1);
+        }
+        conn.setPoints(points);
+    }
+    return true;
+}
+
+/**
+ * Hooks post routing for animation purposes.
+ * @see RoutingListener#postRoute(Connection)
+ */
+public final void postRoute(Connection connection) {
+    if (Animation.isFinalRecording())
+        Animation.hookNeedsCapture(connection, this);
+}
+
+private void reconcileStates(Connection conn) {
+    PointList points1 = cast(PointList)Animation.getInitialState(this, conn);
+    PointList points2 = cast(PointList)Animation.getFinalState(this, conn);
+
+    if (points1 !is null && points1.size() !is points2.size()) {
+        Point p = new Point(), q = new Point();
+
+        int size1 = points1.size() - 1;
+        int size2 = points2.size() - 1;
+
+        int i1 = size1;
+        int i2 = size2;
+
+        double current1 = 1.0;
+        double current2 = 1.0;
+
+        double prev1 = 1.0;
+        double prev2 = 1.0;
+
+        while (i1 > 0 || i2 > 0) {
+            if (Math.abs(current1 - current2) < 0.1
+              && i1 > 0 && i2 > 0) {
+                //Both points are the same, use them and go on;
+                prev1 = current1;
+                prev2 = current2;
+                i1--;
+                i2--;
+                current1 = cast(double)i1 / size1;
+                current2 = cast(double)i2 / size2;
+            } else if (current1 < current2) {
+                //2 needs to catch up
+                // current1 < current2 < prev1
+                points1.getPoint(p, i1);
+                points1.getPoint(q, i1 + 1);
+
+                p.x = cast(int)(((q.x * (current2 - current1) + p.x * (prev1 - current2))
+                    / (prev1 - current1)));
+                p.y = cast(int)(((q.y * (current2 - current1) + p.y * (prev1 - current2))
+                    / (prev1 - current1)));
+
+                points1.insertPoint(p, i1 + 1);
+
+                prev1 = prev2 = current2;
+                i2--;
+                current2 = cast(double)i2 / size2;
+
+            } else {
+                //1 needs to catch up
+                // current2< current1 < prev2
+
+                points2.getPoint(p, i2);
+                points2.getPoint(q, i2 + 1);
+
+                p.x = cast(int)(((q.x * (current1 - current2) + p.x * (prev2 - current1))
+                    / (prev2 - current2)));
+                p.y = cast(int)(((q.y * (current1 - current2) + p.y * (prev2 - current1))
+                    / (prev2 - current2)));
+
+                points2.insertPoint(p, i2 + 1);
+
+                prev2 = prev1 = current1;
+                i1--;
+                current1 = cast(double)i1 / size1;
+            }
+        }
+    }
+}
+
+/**
+ * This callback is unused. Reserved for possible future use.
+ * @see RoutingListener#remove(Connection)
+ */
+public final void remove(Connection connection) { }
+
+/**
+ * Hooks route to intercept routing during animation playback.
+ * @see RoutingListener#route(Connection)
+ */
+public final bool route(Connection conn) {
+    return Animation.isAnimating() && Animation.hookPlayback(conn, this);
+}
+
+/**
+ * This callback is unused. Reserved for possible future use.
+ * @see RoutingListener#setConstraint(Connection, Object)
+ */
+public final void setConstraint(Connection connection, Object constraint) { }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/RoutingListener.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.RoutingListener;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.Connection;
+
+/**
+ * Classes which implement this interface provide callback hooks for various routing
+ * related events.
+ * <P>
+ * Instances can be hooked to a {@link PolylineConnection} by calling {@link
+ * PolylineConnection#addRoutingListener(RoutingListener)}.
+ * @since 3.2
+ */
+public interface RoutingListener {
+
+/**
+ * Called when the connection has been invalidated.
+ * @param connection the connection
+ * @since 3.2
+ */
+void invalidate(Connection connection);
+
+/**
+ * Called after normal routing has completed.
+ * @param connection the routed connection
+ * @since 3.2
+ */
+void postRoute(Connection connection);
+
+/**
+ * Called when a connection has been removed from its router.
+ * @param connection the connection
+ * @since 3.2
+ */
+void remove(Connection connection);
+
+/**
+ * Called prior to routing occurring.  A listener may intercept routing by
+ * returning <code>true</code>.  If intercepted, the connection's
+ * <code>ConnectionRouter</code> will not perform routing.
+ * @param connection the connection being routed
+ * @return <code>true</code> if routing has been performed by the listener
+ * @since 3.2
+ */
+bool route(Connection connection);
+
+/**
+ * Called when the connection's routing constraint has been set or initialized.
+ * @param connection the connection
+ * @param constraint the new constraint
+ * @since 3.2
+ */
+void setConstraint(Connection connection, Object constraint);
+
+/**
+ * A stub implementation which implements all required methods.
+ * @since 3.2
+ */
+class Stub : RoutingListener {
+    public void invalidate(Connection connection) { }
+    public void postRoute(Connection connection) { }
+    public void remove(Connection connection) { }
+    public bool route(Connection connection) {
+        return false;
+    }
+    public void setConstraint(Connection connection, Object constraint) { }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/SWTEventDispatcher.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,575 @@
+/*******************************************************************************
+ * 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.SWTEventDispatcher;
+
+import dwt.dwthelper.utils;
+
+import dwt.DWT;
+import dwt.accessibility.AccessibleControlEvent;
+import dwt.accessibility.AccessibleControlListener;
+import dwt.accessibility.AccessibleEvent;
+import dwt.accessibility.AccessibleListener;
+import dwt.events.DisposeEvent;
+import dwt.events.TraverseEvent;
+import dwt.graphics.Cursor;
+import dwt.widgets.Control;
+import dwtx.draw2d.EventDispatcher;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Figure;
+import dwtx.draw2d.MouseEvent;
+import dwtx.draw2d.FocusEvent;
+import dwtx.draw2d.KeyEvent;
+import dwtx.draw2d.ToolTipHelper;
+static import dwt.widgets.Control;
+import dwtx.draw2d.FocusTraverseManager;
+static import dwt.events.FocusEvent;
+static import dwt.events.KeyEvent;
+static import dwt.events.MouseEvent;
+
+/**
+ * The SWTEventDispatcher provides draw2d with the ability to dispatch DWT Events. The
+ * {@link dwtx.draw2d.LightweightSystem} adds DWT event listeners on its Canvas.
+ * When the Canvas receives an DWT event, it calls the appropriate dispatcher method in
+ * SWTEventDispatcher.
+ */
+public class SWTEventDispatcher
+    : EventDispatcher
+{
+
+/**
+ * Used to tell if any button is pressed without regard to the specific button.
+ * @deprecated Use {@link DWT#BUTTON_MASK} instead.
+ */
+protected static const int ANY_BUTTON = DWT.BUTTON_MASK;
+
+private bool figureTraverse = true;
+
+private bool captured;
+private IFigure root;
+private IFigure mouseTarget;
+private IFigure cursorTarget;
+private IFigure focusOwner;
+private IFigure hoverSource;
+
+private MouseEvent currentEvent;
+private Cursor cursor;
+/** The control this dispatcher is listening to. */
+protected dwt.widgets.Control.Control control;
+
+private ToolTipHelper toolTipHelper;
+private FocusTraverseManager focusManager;
+
+/**
+ * Implements {@link EventDispatcher.AccessibilityDispatcher} but
+ * does nothing in the implementation.
+ */
+protected class FigureAccessibilityDispatcher
+    : AccessibilityDispatcher
+{
+    /** @see AccessibleControlListener#getChildAtPoint(AccessibleControlEvent) */
+    public void getChildAtPoint(AccessibleControlEvent e) { }
+    /** @see AccessibleControlListener#getChildCount(AccessibleControlEvent) */
+    public void getChildCount(AccessibleControlEvent e) { }
+    /** @see AccessibleControlListener#getChildren(AccessibleControlEvent) */
+    public void getChildren(AccessibleControlEvent e) { }
+    /** @see AccessibleControlListener#getDefaultAction(AccessibleControlEvent) */
+    public void getDefaultAction(AccessibleControlEvent e) { }
+    /** @see AccessibleListener#getDescription(AccessibleEvent) */
+    public void getDescription(AccessibleEvent e) { }
+    /** @see AccessibleControlListener#getFocus(AccessibleControlEvent) */
+    public void getFocus(AccessibleControlEvent e) { }
+    /** @see AccessibleListener#getHelp(AccessibleEvent) */
+    public void getHelp(AccessibleEvent e) { }
+    /** @see AccessibleListener#getKeyboardShortcut(AccessibleEvent) */
+    public void getKeyboardShortcut(AccessibleEvent e) { }
+    /** @see AccessibleControlListener#getLocation(AccessibleControlEvent) */
+    public void getLocation(AccessibleControlEvent e) { }
+    /** @see AccessibleListener#getName(AccessibleEvent) */
+    public void getName(AccessibleEvent e) { }
+    /** @see AccessibleControlListener#getRole(AccessibleControlEvent) */
+    public void getRole(AccessibleControlEvent e) { }
+    /** @see AccessibleControlListener#getSelection(AccessibleControlEvent) */
+    public void getSelection(AccessibleControlEvent e) { }
+    /** @see AccessibleControlListener#getState(AccessibleControlEvent) */
+    public void getState(AccessibleControlEvent e) { }
+    /** @see AccessibleControlListener#getValue(AccessibleControlEvent) */
+    public void getValue(AccessibleControlEvent e) { }
+}
+
+public this(){
+    focusManager = new FocusTraverseManager();
+}
+
+/**
+ * @see EventDispatcher#dispatchFocusGained(dwt.events.FocusEvent)
+ */
+public void dispatchFocusGained(dwt.events.FocusEvent.FocusEvent e) {
+    IFigure currentFocusOwner = getFocusTraverseManager().getCurrentFocusOwner();
+
+    /*
+     * Upon focus gained, if there is no current focus owner,
+     * set focus on first focusable child.
+     */
+    if (currentFocusOwner is null)
+        currentFocusOwner =
+                    getFocusTraverseManager().getNextFocusableFigure(root, focusOwner);
+    setFocus(currentFocusOwner);
+}
+
+/**
+ * @see EventDispatcher#dispatchFocusLost(dwt.events.FocusEvent)
+ */
+public void dispatchFocusLost(dwt.events.FocusEvent.FocusEvent e) {
+    setFocus(null);
+}
+
+/**
+ * @see EventDispatcher#dispatchKeyPressed(dwt.events.KeyEvent)
+ */
+public void dispatchKeyPressed(dwt.events.KeyEvent.KeyEvent e) {
+    if (focusOwner !is null) {
+        KeyEvent event = new KeyEvent(this, focusOwner, e);
+        focusOwner.handleKeyPressed(event);
+    }
+}
+
+/**
+ * @see EventDispatcher#dispatchKeyReleased(dwt.events.KeyEvent)
+ */
+public void dispatchKeyReleased(dwt.events.KeyEvent.KeyEvent e) {
+    if (focusOwner !is null) {
+        KeyEvent event = new KeyEvent(this, focusOwner, e);
+        focusOwner.handleKeyReleased(event);
+    }
+}
+
+/**
+ * @see EventDispatcher#dispatchKeyTraversed(TraverseEvent)
+ */
+public void dispatchKeyTraversed(TraverseEvent e) {
+    if (!figureTraverse)
+        return;
+    IFigure nextFigure = null;
+
+    if (e.detail is DWT.TRAVERSE_TAB_NEXT)
+        nextFigure = getFocusTraverseManager().getNextFocusableFigure(root, focusOwner);
+    else if (e.detail is DWT.TRAVERSE_TAB_PREVIOUS)
+        nextFigure =
+                getFocusTraverseManager().getPreviousFocusableFigure(root, focusOwner);
+
+    if (nextFigure !is null) {
+        e.doit = false;
+        setFocus(nextFigure);
+    }
+}
+
+/**
+ * @see EventDispatcher#dispatchMouseHover(dwt.events.MouseEvent)
+ */
+public void dispatchMouseHover(dwt.events.MouseEvent.MouseEvent me) {
+    receive(me);
+    if (mouseTarget !is null)
+        mouseTarget.handleMouseHover(currentEvent);
+    /*
+     * Check Tooltip source.
+     * Get Tooltip source's Figure.
+     * Set that tooltip as the lws contents on the helper.
+     */
+    if (hoverSource !is null) {
+        toolTipHelper = getToolTipHelper();
+        IFigure tip = hoverSource.getToolTip();
+        Control control = cast(Control)me.getSource();
+        dwt.graphics.Point.Point absolute;
+        absolute = control.toDisplay(new dwt.graphics.Point.Point(me.x, me.y));
+        toolTipHelper.displayToolTipNear(hoverSource, tip, absolute.x, absolute.y);
+    }
+}
+
+/**
+ * @see EventDispatcher#dispatchMouseDoubleClicked(dwt.events.MouseEvent)
+ */
+public void dispatchMouseDoubleClicked(dwt.events.MouseEvent.MouseEvent me) {
+    receive(me);
+    if (mouseTarget !is null)
+        mouseTarget.handleMouseDoubleClicked(currentEvent);
+}
+
+/**
+ * @see EventDispatcher#dispatchMouseEntered(dwt.events.MouseEvent)
+ */
+public void dispatchMouseEntered(dwt.events.MouseEvent.MouseEvent me) {
+    receive(me);
+}
+
+/**
+ * @see EventDispatcher#dispatchMouseExited(dwt.events.MouseEvent)
+ */
+public void dispatchMouseExited(dwt.events.MouseEvent.MouseEvent me) {
+    setHoverSource(null, me);
+    if (mouseTarget !is null) {
+        currentEvent =
+                new MouseEvent(me.x, me.y, this, mouseTarget, me.button, me.stateMask);
+        mouseTarget.handleMouseExited(currentEvent);
+        releaseCapture();
+        mouseTarget = null;
+    }
+}
+
+/**
+ * @see EventDispatcher#dispatchMousePressed(dwt.events.MouseEvent)
+ */
+public void dispatchMousePressed(dwt.events.MouseEvent.MouseEvent me) {
+    receive(me);
+    if (mouseTarget !is null) {
+        mouseTarget.handleMousePressed(currentEvent);
+        if (currentEvent.isConsumed())
+            setCapture(mouseTarget);
+    }
+}
+
+/**
+ * @see EventDispatcher#dispatchMouseMoved(dwt.events.MouseEvent)
+ */
+public void dispatchMouseMoved(dwt.events.MouseEvent.MouseEvent me) {
+    receive(me);
+    if (mouseTarget !is null) {
+        if ((me.stateMask & DWT.BUTTON_MASK) !is 0)
+            mouseTarget.handleMouseDragged(currentEvent);
+        else
+            mouseTarget.handleMouseMoved(currentEvent);
+    }
+}
+
+/**
+ * @see EventDispatcher#dispatchMouseReleased(dwt.events.MouseEvent)
+ */
+public void dispatchMouseReleased(dwt.events.MouseEvent.MouseEvent me) {
+    receive(me);
+    if (mouseTarget !is null) {
+        mouseTarget.handleMouseReleased(currentEvent);
+    }
+    releaseCapture();
+    receive(me);
+}
+
+/**
+ * @see EventDispatcher#getAccessibilityDispatcher()
+ */
+protected AccessibilityDispatcher getAccessibilityDispatcher() {
+    return null;
+}
+
+/**
+ * Returns the current mouse event.
+ * @return the current mouse event; can be <code>null</code>
+ */
+protected MouseEvent getCurrentEvent() {
+    return currentEvent;
+}
+
+private IFigure getCurrentToolTip() {
+    if (hoverSource !is null)
+        return hoverSource.getToolTip();
+    else
+        return null;
+}
+
+/**
+ * Returns the figure that the cursor is over.
+ * @return the cursor target
+ */
+protected IFigure getCursorTarget() {
+    return cursorTarget;
+}
+
+/**
+ * Returns the ToolTipHelper used to display tooltips on hover events.
+ * @return the ToolTipHelper
+ */
+protected ToolTipHelper getToolTipHelper() {
+    if (toolTipHelper is null)
+        toolTipHelper = new ToolTipHelper(control);
+    return toolTipHelper;
+}
+
+/**
+ * Returns the FocusTraverseManager which is used to determine which figure will get focus
+ * when a TAB or ALT+TAB key sequence occurs.
+ * @return the FocusTraverseManager
+ */
+protected final FocusTraverseManager getFocusTraverseManager() {
+    if (focusManager is null) {
+        focusManager = new FocusTraverseManager();
+    }
+    return focusManager;
+}
+
+/**
+ * @see EventDispatcher#getFocusOwner()
+ */
+/*package*/ IFigure getFocusOwner() {
+    return focusOwner;
+}
+
+/**
+ * Returns the figure that is the target of mouse events.  This may not be the figure
+ * beneath the cursor because another figure may have captured the mouse and will continue
+ * to get mouse events until capture is released.
+ * @return the mouse target
+ */
+protected IFigure getMouseTarget() {
+    return mouseTarget;
+}
+
+/**
+ * Returns the root figure for this dispatcher.
+ * @return the root figure
+ */
+protected IFigure getRoot() {
+    return root;
+}
+
+/**
+ * @see EventDispatcher#isCaptured()
+ */
+public bool isCaptured() {
+    return captured;
+}
+
+private void receive(dwt.events.MouseEvent.MouseEvent me) {
+    currentEvent = null;
+    updateFigureUnderCursor(me);
+    int state = me.stateMask;
+    if (captured) {
+        if (mouseTarget !is null)
+            currentEvent = new MouseEvent(me.x, me.y, this, mouseTarget, me.button, state);
+    } else {
+        IFigure f = root.findMouseEventTargetAt(me.x, me.y);
+        if (f is mouseTarget) {
+            if (mouseTarget !is null)
+                currentEvent =
+                        new MouseEvent(me.x, me.y, this, mouseTarget, me.button, state);
+            return;
+        }
+        if (mouseTarget !is null) {
+            currentEvent = new MouseEvent(me.x, me.y, this, mouseTarget, me.button, state);
+            mouseTarget.handleMouseExited(currentEvent);
+        }
+        setMouseTarget(f);
+        if (mouseTarget !is null) {
+            currentEvent = new MouseEvent(me.x, me.y, this, mouseTarget, me.button, state);
+            mouseTarget.handleMouseEntered(currentEvent);
+        }
+    }
+}
+
+/**
+ * @see EventDispatcher#releaseCapture()
+ */
+protected void releaseCapture() {
+    captured = false;
+}
+
+/**
+ * @see EventDispatcher#requestFocus(IFigure)
+ */
+public void requestFocus(IFigure fig) {
+    setFocus(fig);
+}
+
+/**
+ * @see EventDispatcher#requestRemoveFocus(IFigure)
+ */
+public void requestRemoveFocus(IFigure fig) {
+    if (getFocusOwner() is fig)
+        setFocus(null);
+    if (mouseTarget is fig)
+        mouseTarget = null;
+    if (cursorTarget is fig)
+        cursorTarget = null;
+    if (hoverSource is fig)
+        hoverSource = null;
+    getFocusTraverseManager().setCurrentFocusOwner(null);
+}
+
+/**
+ * @see EventDispatcher#setCapture(IFigure)
+ */
+protected void setCapture(IFigure figure) {
+    captured = true;
+    mouseTarget = figure;
+}
+
+/**
+ * @see EventDispatcher#setControl(Control)
+ */
+public void setControl(Control c) {
+    if (c is control)
+        return;
+    if (control !is null && !control.isDisposed())
+        throw new RuntimeException(
+                "Can not set control again once it has been set"); //$NON-NLS-1$
+    if (c !is null)
+        c.addDisposeListener(new class() dwt.events.DisposeListener.DisposeListener {
+            public void widgetDisposed(DisposeEvent e) {
+                if (toolTipHelper !is null)
+                    toolTipHelper.dispose();
+            }
+        });
+    control = c;
+}
+
+/**
+ * Sets the mouse cursor.
+ * @param c the new cursor
+ */
+protected void setCursor(Cursor c) {
+    if (c is null && cursor is null) {
+        return;
+    } else if ((c !is cursor) || (!c.opEquals(cursor))) {
+        cursor = c;
+        if (control !is null && !control.isDisposed())
+            control.setCursor(c);
+    }
+}
+
+/**
+ * Enables key traversal via TAB and ALT+TAB if <i>traverse</i> is <code>true</code>.
+ * Disables it otherwise.
+ * @param traverse whether key traversal should be enabled
+ */
+public void setEnableKeyTraversal(bool traverse) {
+    figureTraverse = traverse;
+}
+
+/**
+ * Sets the figure under the mouse cursor.
+ * @param f the new figure under the cursor
+ */
+protected void setFigureUnderCursor(IFigure f) {
+    if (cursorTarget is f)
+        return;
+    cursorTarget = f;
+    updateCursor();
+}
+
+/**
+ * Sets the focus figure.  If the figure currently with focus is not <code>null</code>,
+ * {@link IFigure#handleFocusLost(FocusEvent)} is called on the current focused figure. If
+ * the new focus figure is not <code>null</code>, this will call
+ * {@link IFigure#handleFocusGained(FocusEvent)} on the new focused figure.
+ * @param fig the new focus figure
+ */
+protected void setFocus(IFigure fig) {
+    if (fig is focusOwner)
+        return;
+    FocusEvent fe = new FocusEvent(focusOwner, fig);
+    IFigure oldOwner = focusOwner;
+    focusOwner = fig;
+    if (oldOwner !is null)
+        oldOwner.handleFocusLost(fe);
+    if (fig !is null)
+        getFocusTraverseManager().setCurrentFocusOwner(fig);
+    if (focusOwner !is null)
+        focusOwner.handleFocusGained(fe);
+}
+
+/**
+ * Sets the figure that the mouse cursor is hovering over.
+ * @param figure the new hover source
+ * @param me the mouse event
+ */
+protected void setHoverSource(Figure figure, dwt.events.MouseEvent.MouseEvent me) {
+    hoverSource = figure;
+    if (figure !is null) {
+        Control control = cast(Control)me.getSource();
+        dwt.graphics.Point.Point absolute;
+        absolute = control.toDisplay(new dwt.graphics.Point.Point(me.x, me.y));
+        toolTipHelper = getToolTipHelper();
+        toolTipHelper.updateToolTip(
+                            hoverSource, getCurrentToolTip(), absolute.x, absolute.y);
+    } else if (toolTipHelper !is null) {
+        // Update with null to clear hoverSource in ToolTipHelper
+        toolTipHelper.updateToolTip(hoverSource, getCurrentToolTip(), me.x, me.y);
+    }
+}
+
+/**
+ * Sets the given figure to be the target of future mouse events.
+ * @param figure the new mouse target
+ */
+protected void setMouseTarget(IFigure figure) {
+    mouseTarget = figure;
+}
+
+/**
+ * @see EventDispatcher#setRoot(IFigure)
+ */
+public void setRoot(IFigure figure) {
+    root = figure;
+}
+
+/**
+ * @see EventDispatcher#updateCursor()
+ */
+protected void updateCursor() {
+    Cursor newCursor = null;
+    if (cursorTarget !is null)
+        newCursor = cursorTarget.getCursor();
+    setCursor(newCursor);
+}
+
+/**
+ * Updates the figure under the cursor, unless the mouse is captured, in which case all
+ * mouse events will be routed to the figure that captured the mouse.
+ * @param me the mouse event
+ */
+protected void updateFigureUnderCursor(dwt.events.MouseEvent.MouseEvent me) {
+    if (!captured) {
+        IFigure f = root.findFigureAt(me.x, me.y);
+        setFigureUnderCursor(f);
+        if (cast(Figure)cursorTarget !is hoverSource)
+            updateHoverSource(me);
+    }
+}
+
+/**
+ * Updates the figure that will receive hover events.  The hover source must have a
+ * tooltip.  If the figure under the mouse doesn't have a tooltip set, this method will
+ * walk up the ancestor hierarchy until either a figure with a tooltip is found or it
+ * gets to the root figure.
+ * @param me the mouse event
+ */
+protected void updateHoverSource(dwt.events.MouseEvent.MouseEvent me) {
+    /*
+     * Derive source from figure under cursor.
+     * Set the source in setHoverSource();
+     * If figure.getToolTip() is null, get parent's toolTip
+     * Continue parent traversal until a toolTip is found or root is reached.
+     */
+    if (cursorTarget !is null) {
+        bool sourceFound = false;
+        Figure source = cast(Figure)cursorTarget;
+        while (!sourceFound && source.getParent() !is null) {
+            if (source.getToolTip() !is null)
+                sourceFound = true;
+            else
+                source = cast(Figure)source.getParent();
+        }
+        setHoverSource(source, me);
+    } else {
+        setHoverSource(null, me);
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/SWTGraphics.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,1220 @@
+/*******************************************************************************
+ * 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.SWTGraphics;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+static import dwt.graphics.Rectangle;
+import dwt.DWT;
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.FontMetrics;
+import dwt.graphics.GC;
+import dwt.graphics.Image;
+import dwt.graphics.Path;
+import dwt.graphics.Pattern;
+import dwt.graphics.TextLayout;
+import dwt.graphics.Transform;
+import dwt.widgets.Display;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.Graphics;
+
+/**
+ * A concrete implementation of <code>Graphics</code> using an DWT
+ * <code>GC</code>. There are 2 states contained in this graphics class -- the
+ * applied state which is the actual state of the GC and the current state which
+ * is the current state of this graphics object.  Certain properties can be
+ * changed multiple times and the GC won't be updated until it's actually used.
+ * <P>
+ * WARNING: This class is not intended to be subclassed.
+ */
+public class SWTGraphics
+    : Graphics
+{
+
+/**
+ * An internal type used to represent and update the GC's clipping.
+ * @since 3.1
+ */
+interface Clipping {
+    /**
+     * Sets the clip's bounding rectangle into the provided argument and returns it for convenince.
+     * @param rect the rect
+     * @return the given rect
+     * @since 3.1
+     */
+    Rectangle getBoundingBox(Rectangle rect);
+    Clipping getCopy();
+    void intersect(int left, int top, int right, int bottom);
+    void scale(float horizontal, float vertical);
+    void setOn(GC gc, int translateX, int translateY);
+    void translate(float dx, float dy);
+}
+
+/**
+ * Any state stored in this class is only applied when it is needed by a
+ * specific graphics call.
+ * @since 3.1
+ */
+static class LazyState {
+    Color bgColor;
+    Color fgColor;
+    Font font;
+    int graphicHints;
+    int lineWidth;
+    Clipping relativeClip;
+}
+
+static class RectangleClipping : Clipping {
+    private float top, left, bottom, right;
+
+    this(float left, float top, float right, float bottom) {
+        this.left = left;
+        this.right = right;
+        this.bottom = bottom;
+        this.top = top;
+    }
+
+    this(dwt.graphics.Rectangle.Rectangle rect) {
+        left = rect.x;
+        top = rect.y;
+        right = rect.x + rect.width;
+        bottom = rect.y + rect.height;
+    }
+
+    this(Rectangle rect) {
+        left = rect.x;
+        top = rect.y;
+        right = rect.right();
+        bottom = rect.bottom();
+    }
+
+    public Rectangle getBoundingBox(Rectangle rect) {
+        rect.x = cast(int)left;
+        rect.y = cast(int)top;
+        rect.width = cast(int)Math.ceil(right) - rect.x;
+        rect.height = cast(int)Math.ceil(bottom) - rect.y;
+        return rect;
+    }
+
+    public Clipping getCopy() {
+        return new RectangleClipping(left, top, right, bottom);
+    }
+
+    public void intersect(int left, int top, int right, int bottom) {
+        this.left = Math.max(this.left, left);
+        this.right = Math.min(this.right, right);
+        this.top = Math.max(this.top, top);
+        this.bottom = Math.min(this.bottom, bottom);
+        if (right < left || bottom < top) {
+            //width and height of -1 to avoid ceiling function from re-adding a pixel.
+            this.right = left - 1;
+            this.bottom = top - 1;
+        }
+    }
+
+    public void scale(float horz, float vert) {
+        left /= horz;
+        right /= horz;
+        top /= vert;
+        bottom /= vert;
+    }
+
+    public void setOn(GC gc, int translateX, int translateY) {
+        int xInt = cast(int)Math.floor(left);
+        int yInt = cast(int)Math.floor(top);
+        gc.setClipping(xInt + translateX, yInt + translateY,
+                cast(int)Math.ceil(right) - xInt,
+                cast(int)Math.ceil(bottom) - yInt);
+    }
+
+    public void translate(float dx, float dy) {
+        left += dx;
+        right += dx;
+        top += dy;
+        bottom += dy;
+    }
+}
+
+/**
+ * Contains the entire state of the Graphics.
+ */
+static class State
+    : LazyState
+    , Cloneable
+{
+    float affineMatrix[];
+    int alpha;
+    Pattern bgPattern;
+    int dx, dy;
+
+    Pattern fgPattern;
+    int lineDash[];
+
+    this(){
+    }
+
+    public Object clone() {
+        auto res = new State;
+        res.affineMatrix = this.affineMatrix.dup;
+        res.alpha        = this.alpha;
+        res.bgPattern    = this.bgPattern;
+        res.dx           = this.dx;
+        res.dy           = this.dy;
+        res.fgPattern    = this.fgPattern;
+        res.lineDash     = this.lineDash.dup;
+        return res;
+    }
+
+    /**
+     * Copies all state information from the given State to this State
+     * @param state The State to copy from
+     */
+    public void copyFrom(State state) {
+        bgColor = state.bgColor;
+        fgColor = state.fgColor;
+        lineWidth = state.lineWidth;
+        dx = state.dx;
+        dy = state.dy;
+        bgPattern = state.bgPattern;
+        fgPattern = state.fgPattern;
+        font = state.font;
+        lineDash = state.lineDash;
+        graphicHints = state.graphicHints;
+        affineMatrix = state.affineMatrix;
+        relativeClip = state.relativeClip;
+        alpha = state.alpha;
+    }
+}
+
+static const int AA_MASK;
+static const int AA_SHIFT;
+static const int AA_WHOLE_NUMBER = 1;
+static const int ADVANCED_GRAPHICS_MASK;
+static const int ADVANCED_HINTS_DEFAULTS;
+static const int ADVANCED_HINTS_MASK;
+static const int ADVANCED_SHIFT;
+static const int CAP_MASK;
+static const int CAP_SHIFT;
+static const int FILL_RULE_MASK;
+static const int FILL_RULE_SHIFT;
+static const int FILL_RULE_WHOLE_NUMBER = -1;
+static const int INTERPOLATION_MASK;
+static const int INTERPOLATION_SHIFT;
+static const int INTERPOLATION_WHOLE_NUMBER = 1;
+static const int JOIN_MASK;
+static const int JOIN_SHIFT;
+static const int LINE_STYLE_MASK;
+
+static const int TEXT_AA_MASK;
+static const int TEXT_AA_SHIFT;
+static const int XOR_MASK;
+static const int XOR_SHIFT;
+
+static this() {
+    XOR_SHIFT = 3;
+    CAP_SHIFT = 4;
+    JOIN_SHIFT = 6;
+    AA_SHIFT = 8;
+    TEXT_AA_SHIFT = 10;
+    INTERPOLATION_SHIFT = 12;
+    FILL_RULE_SHIFT = 14;
+    ADVANCED_SHIFT = 15;
+
+    LINE_STYLE_MASK = 7;
+    AA_MASK = 3 << AA_SHIFT;
+    CAP_MASK = 3 << CAP_SHIFT;
+    FILL_RULE_MASK = 1 << FILL_RULE_SHIFT;
+    INTERPOLATION_MASK = 3 << INTERPOLATION_SHIFT;
+    JOIN_MASK = 3 << JOIN_SHIFT;
+    TEXT_AA_MASK = 3 << TEXT_AA_SHIFT;
+    XOR_MASK = 1 << XOR_SHIFT;
+    ADVANCED_GRAPHICS_MASK = 1 << ADVANCED_SHIFT;
+
+    ADVANCED_HINTS_MASK = TEXT_AA_MASK | AA_MASK | INTERPOLATION_MASK;
+    ADVANCED_HINTS_DEFAULTS = ((DWT.DEFAULT + AA_WHOLE_NUMBER) << TEXT_AA_SHIFT)
+            | ((DWT.DEFAULT + AA_WHOLE_NUMBER) << AA_SHIFT)
+            | ((DWT.DEFAULT + INTERPOLATION_WHOLE_NUMBER) << INTERPOLATION_SHIFT);
+}
+
+private const LazyState appliedState;
+private const State currentState;
+
+private bool elementsNeedUpdate;
+private GC gc;
+
+private bool sharedClipping;
+private List stack;
+
+private int stackPointer = 0;
+Transform transform;
+private int translateX = 0;
+private int translateY = 0;
+
+/**
+ * Constructs a new SWTGraphics that draws to the Canvas using the given GC.
+ * @param gc the GC
+ */
+public this(GC gc) {
+    appliedState = new LazyState();
+    currentState = new State();
+    stack = new ArrayList();
+    this.gc = gc;
+    init();
+}
+
+/**
+ * If the background color has changed, this change will be pushed to the GC.  Also calls
+ * {@link #checkGC()}.
+ */
+protected final void checkFill() {
+    if (!currentState.bgColor.opEquals(appliedState.bgColor) && currentState.bgPattern is null)
+        gc.setBackground(appliedState.bgColor = currentState.bgColor);
+    checkGC();
+}
+
+/**
+ * If the rendering hints or the clip region has changed, these changes will be pushed to
+ * the GC. Rendering hints include anti-alias, xor, join, cap, line style, fill rule,
+ * interpolation, and other settings.
+ */
+protected /+final+/ void checkGC() {
+    if (appliedState.relativeClip !is currentState.relativeClip) {
+        appliedState.relativeClip = currentState.relativeClip;
+        currentState.relativeClip.setOn(gc, translateX, translateY);
+    }
+    if (appliedState.graphicHints !is currentState.graphicHints) {
+        reconcileHints(appliedState.graphicHints, currentState.graphicHints);
+        appliedState.graphicHints = currentState.graphicHints;
+    }
+}
+
+/**
+ * If the line width, line style, foreground or background colors have changed, these
+ * changes will be pushed to the GC.  Also calls {@link #checkGC()}.
+ */
+protected /+final+/ void checkPaint() {
+    checkGC();
+    if (!currentState.fgColor.opEquals(appliedState.fgColor) && currentState.fgPattern is null)
+        gc.setForeground(appliedState.fgColor = currentState.fgColor);
+    if (appliedState.lineWidth !is currentState.lineWidth)
+        gc.setLineWidth(appliedState.lineWidth = currentState.lineWidth);
+    if (!currentState.bgColor.opEquals(appliedState.bgColor) && currentState.bgPattern is null)
+        gc.setBackground(appliedState.bgColor = currentState.bgColor);
+}
+
+/**
+ * @since 3.1
+ */
+private void checkSharedClipping() {
+    if (sharedClipping) {
+        sharedClipping = false;
+
+        bool previouslyApplied = (appliedState is currentState.relativeClip);
+        currentState.relativeClip = currentState.relativeClip.getCopy();
+        if (previouslyApplied)
+            appliedState.relativeClip = currentState.relativeClip;
+    }
+}
+
+/**
+ * If the font has changed, this change will be pushed to the GC.  Also calls
+ * {@link #checkPaint()} and {@link #checkFill()}.
+ */
+protected /+final+/ void checkText() {
+    checkPaint();
+    if (!appliedState.font.opEquals(currentState.font))
+        gc.setFont(appliedState.font = currentState.font);
+}
+
+/**
+ * @see Graphics#clipRect(Rectangle)
+ */
+public void clipRect(Rectangle rect) {
+    if (currentState.relativeClip is null)
+        throw new IllegalStateException("The current clipping area does not " //$NON-NLS-1$
+        "support intersection."); //$NON-NLS-1$
+    checkSharedClipping();
+    currentState.relativeClip.intersect(rect.x, rect.y, rect.right(), rect.bottom());
+    appliedState.relativeClip = null;
+}
+
+/**
+ * @see Graphics#dispose()
+ */
+public void dispose() {
+    while (stackPointer > 0)
+        popState();
+    if (transform !is null)
+        transform.dispose();
+}
+
+/**
+ * @see Graphics#drawArc(int, int, int, int, int, int)
+ */
+public void drawArc(int x, int y, int width, int height, int offset, int length) {
+    checkPaint();
+    gc.drawArc(x + translateX, y + translateY, width, height, offset, length);
+}
+
+/**
+ * @see Graphics#drawFocus(int, int, int, int)
+ */
+public void drawFocus(int x, int y, int w, int h) {
+    checkPaint();
+    gc.drawFocus(x + translateX, y + translateY, w + 1, h + 1);
+}
+
+/**
+ * @see Graphics#drawImage(Image, int, int)
+ */
+public void drawImage(Image srcImage, int x, int y) {
+    checkGC();
+    gc.drawImage(srcImage, x + translateX, y + translateY);
+}
+
+/**
+ * @see Graphics#drawImage(Image, int, int, int, int, int, int, int, int)
+ */
+public void drawImage(Image srcImage,
+        int x1, int y1, int w1, int h1,
+        int x2, int y2, int w2, int h2) {
+    checkGC();
+    gc.drawImage(srcImage, x1, y1, w1, h1, x2 + translateX, y2 + translateY, w2, h2);
+}
+
+/**
+ * @see Graphics#drawLine(int, int, int, int)
+ */
+public void drawLine(int x1, int y1, int x2, int y2) {
+    checkPaint();
+    gc.drawLine(x1 + translateX, y1 + translateY, x2 + translateX, y2 + translateY);
+}
+
+/**
+ * @see Graphics#drawOval(int, int, int, int)
+ */
+public void drawOval(int x, int y, int width, int height) {
+    checkPaint();
+    gc.drawOval(x + translateX, y + translateY, width, height);
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#drawPath(Path)
+ */
+public void drawPath(Path path) {
+    checkPaint();
+    initTransform(false);
+    gc.drawPath(path);
+}
+
+/**
+ * @see Graphics#drawPoint(int, int)
+ */
+public void drawPoint(int x, int y) {
+    checkPaint();
+    gc.drawPoint(x + translateX, y + translateY);
+}
+
+/**
+ * @see Graphics#drawPolygon(int[])
+ */
+public void drawPolygon(int[] points) {
+    checkPaint();
+    try {
+        translatePointArray(points, translateX, translateY);
+        gc.drawPolygon(points);
+    } finally {
+        translatePointArray(points, -translateX, -translateY);
+    }
+}
+
+/**
+ * @see Graphics#drawPolygon(PointList)
+ */
+public void drawPolygon(PointList points) {
+    drawPolygon(points.toIntArray());
+}
+
+/**
+ * @see Graphics#drawPolyline(int[])
+ */
+public void drawPolyline(int[] points) {
+    checkPaint();
+    try {
+        translatePointArray(points, translateX, translateY);
+        gc.drawPolyline(points);
+    } finally {
+        translatePointArray(points, -translateX, -translateY);
+    }
+}
+
+/**
+ * @see Graphics#drawPolyline(PointList)
+ */
+public void drawPolyline(PointList points) {
+    drawPolyline(points.toIntArray());
+}
+
+/**
+ * @see Graphics#drawRectangle(int, int, int, int)
+ */
+public void drawRectangle(int x, int y, int width, int height) {
+    checkPaint();
+    gc.drawRectangle(x + translateX, y + translateY, width, height);
+}
+
+/**
+ * @see Graphics#drawRoundRectangle(Rectangle, int, int)
+ */
+public void drawRoundRectangle(Rectangle r, int arcWidth, int arcHeight) {
+    checkPaint();
+    gc.drawRoundRectangle(r.x + translateX, r.y + translateY, r.width, r.height,
+                            arcWidth, arcHeight);
+}
+
+/**
+ * @see Graphics#drawString(String, int, int)
+ */
+public void drawString(String s, int x, int y) {
+    checkText();
+    gc.drawString(s, x + translateX, y + translateY, true);
+}
+
+/**
+ * @see Graphics#drawText(String, int, int)
+ */
+public void drawText(String s, int x, int y) {
+    checkText();
+    gc.drawText(s, x + translateX, y + translateY, true);
+}
+
+/**
+ * @see Graphics#drawTextLayout(TextLayout, int, int, int, int, Color, Color)
+ */
+public void drawTextLayout(TextLayout layout, int x, int y, int selectionStart,
+        int selectionEnd, Color selectionForeground, Color selectionBackground) {
+    //$TODO probably just call checkPaint since Font and BG color don't apply
+    checkText();
+    layout.draw(gc, x + translateX, y + translateY, selectionStart, selectionEnd,
+            selectionForeground, selectionBackground);
+}
+
+/**
+ * @see Graphics#fillArc(int, int, int, int, int, int)
+ */
+public void fillArc(int x, int y, int width, int height, int offset, int length) {
+    checkFill();
+    gc.fillArc(x + translateX, y + translateY, width, height, offset, length);
+}
+
+/**
+ * @see Graphics#fillGradient(int, int, int, int, bool)
+ */
+public void fillGradient(int x, int y, int w, int h, bool vertical) {
+    checkPaint();
+    gc.fillGradientRectangle(x + translateX, y + translateY, w, h, vertical);
+}
+
+/**
+ * @see Graphics#fillOval(int, int, int, int)
+ */
+public void fillOval(int x, int y, int width, int height) {
+    checkFill();
+    gc.fillOval(x + translateX, y + translateY, width, height);
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#fillPath(Path)
+ */
+public void fillPath(Path path) {
+    checkFill();
+    initTransform(false);
+    gc.fillPath(path);
+}
+
+/**
+ * @see Graphics#fillPolygon(int[])
+ */
+public void fillPolygon(int[] points) {
+    checkFill();
+    try {
+        translatePointArray(points, translateX, translateY);
+        gc.fillPolygon(points);
+    } finally {
+        translatePointArray(points, -translateX, -translateY);
+    }
+}
+
+/**
+ * @see Graphics#fillPolygon(PointList)
+ */
+public void fillPolygon(PointList points) {
+    fillPolygon(points.toIntArray());
+}
+
+/**
+ * @see Graphics#fillRectangle(int, int, int, int)
+ */
+public void fillRectangle(int x, int y, int width, int height) {
+    checkFill();
+    gc.fillRectangle(x + translateX, y + translateY, width, height);
+}
+
+/**
+ * @see Graphics#fillRoundRectangle(Rectangle, int, int)
+ */
+public void fillRoundRectangle(Rectangle r, int arcWidth, int arcHeight) {
+    checkFill();
+    gc.fillRoundRectangle(r.x + translateX, r.y + translateY, r.width, r.height,
+                            arcWidth, arcHeight);
+}
+
+/**
+ * @see Graphics#fillString(String, int, int)
+ */
+public void fillString(String s, int x, int y) {
+    checkText();
+    gc.drawString(s, x + translateX, y + translateY, false);
+}
+
+/**
+ * @see Graphics#fillText(String, int, int)
+ */
+public void fillText(String s, int x, int y) {
+    checkText();
+    gc.drawText(s, x + translateX, y + translateY, false);
+}
+
+/**
+ * @see Graphics#getAlpha()
+ */
+public int getAlpha() {
+    return currentState.alpha;
+}
+
+/**
+ * @see Graphics#getAntialias()
+ */
+public int getAntialias() {
+    return ((currentState.graphicHints & AA_MASK) >> AA_SHIFT) - AA_WHOLE_NUMBER;
+}
+
+/**
+ * @see Graphics#getBackgroundColor()
+ */
+public Color getBackgroundColor() {
+    return currentState.bgColor;
+}
+
+/**
+ * @see Graphics#getClip(Rectangle)
+ */
+public Rectangle getClip(Rectangle rect) {
+    if (currentState.relativeClip !is null) {
+        currentState.relativeClip.getBoundingBox(rect);
+        return rect;
+    }
+    throw new IllegalStateException(
+            "Clipping can no longer be queried due to transformations"); //$NON-NLS-1$
+}
+
+/**
+ * @see Graphics#getFillRule()
+ */
+public int getFillRule() {
+    return ((currentState.graphicHints & FILL_RULE_MASK) >> FILL_RULE_SHIFT) - FILL_RULE_WHOLE_NUMBER;
+}
+
+/**
+ * @see Graphics#getFont()
+ */
+public Font getFont() {
+    return currentState.font;
+}
+
+/**
+ * @see Graphics#getFontMetrics()
+ */
+public FontMetrics getFontMetrics() {
+    checkText();
+    return gc.getFontMetrics();
+}
+
+/**
+ * @see Graphics#getForegroundColor()
+ */
+public Color getForegroundColor() {
+    return currentState.fgColor;
+}
+
+/**
+ * @see Graphics#getInterpolation()
+ */
+public int getInterpolation() {
+    return ((currentState.graphicHints & INTERPOLATION_MASK) >> INTERPOLATION_SHIFT)
+            - INTERPOLATION_WHOLE_NUMBER;
+}
+
+/**
+ * @see Graphics#getLineCap()
+ */
+public int getLineCap() {
+    return (currentState.graphicHints & CAP_MASK) >> CAP_SHIFT;
+}
+
+/**
+ * @see Graphics#getLineJoin()
+ */
+public int getLineJoin() {
+    return (currentState.graphicHints & JOIN_MASK) >> JOIN_SHIFT;
+}
+
+/**
+ * @see Graphics#getLineStyle()
+ */
+public int getLineStyle() {
+    return currentState.graphicHints & LINE_STYLE_MASK;
+}
+
+/**
+ * @see Graphics#getLineWidth()
+ */
+public int getLineWidth() {
+    return currentState.lineWidth;
+}
+
+/**
+ * @see Graphics#getTextAntialias()
+ */
+public int getTextAntialias() {
+    return ((currentState.graphicHints & TEXT_AA_MASK) >> TEXT_AA_SHIFT) - AA_WHOLE_NUMBER;
+}
+
+/**
+ * @see Graphics#getXORMode()
+ */
+public bool getXORMode() {
+    return (currentState.graphicHints & XOR_MASK) !is 0;
+}
+
+/**
+ * Called by constructor, initializes all State information for currentState
+ */
+protected void init() {
+    currentState.bgColor = appliedState.bgColor = gc.getBackground();
+    currentState.fgColor = appliedState.fgColor = gc.getForeground();
+    currentState.font = appliedState.font = gc.getFont();
+    currentState.lineWidth = appliedState.lineWidth = gc.getLineWidth();
+    currentState.graphicHints |= gc.getLineStyle();
+    currentState.graphicHints |= gc.getLineCap() << CAP_SHIFT;
+    currentState.graphicHints |= gc.getLineJoin() << JOIN_SHIFT;
+    currentState.graphicHints |= gc.getAdvanced() ? ADVANCED_GRAPHICS_MASK : 0;
+    currentState.graphicHints |= gc.getXORMode() ? XOR_MASK : 0;
+
+    appliedState.graphicHints = currentState.graphicHints;
+
+    currentState.relativeClip = new RectangleClipping(gc.getClipping());
+    currentState.lineDash = gc.getLineDash();
+    currentState.alpha = gc.getAlpha();
+}
+
+private void initTransform(bool force) {
+    if (!force && translateX is 0 && translateY is 0)
+        return;
+    if (transform is null) {
+        transform = new Transform(Display.getCurrent());
+        elementsNeedUpdate = true;
+        transform.translate(translateX, translateY);
+        translateX = 0;
+        translateY = 0;
+        gc.setTransform(transform);
+        currentState.graphicHints |= ADVANCED_GRAPHICS_MASK;
+    }
+}
+
+/**
+ * @see Graphics#popState()
+ */
+public void popState() {
+    stackPointer--;
+    restoreState(cast(State)stack.get(stackPointer));
+}
+
+/**
+ * @see Graphics#pushState()
+ */
+public void pushState() {
+    if (currentState.relativeClip is null)
+        throw new IllegalStateException("The clipping has been modified in" //$NON-NLS-1$
+                "a way that cannot be saved and restored."); //$NON-NLS-1$
+//     try {
+        State s;
+        currentState.dx = translateX;
+        currentState.dy = translateY;
+
+        if (elementsNeedUpdate) {
+            elementsNeedUpdate = false;
+            transform.getElements(currentState.affineMatrix = new float[6]);
+        }
+        if (stack.size() > stackPointer) {
+            s = cast(State)stack.get(stackPointer);
+            s.copyFrom(currentState);
+        } else {
+            stack.add(currentState.clone());
+        }
+        sharedClipping = true;
+        stackPointer++;
+//     } catch (CloneNotSupportedException e) {
+//         throw new RuntimeException(e);
+//     }
+}
+
+private void reconcileHints(int applied, int hints) {
+    if (applied !is hints) {
+        int changes = hints ^ applied;
+
+        if ((changes & LINE_STYLE_MASK) !is 0)
+            gc.setLineStyle(hints & LINE_STYLE_MASK);
+
+        if ((changes & XOR_MASK) !is 0)
+            gc.setXORMode((hints & XOR_MASK) !is 0);
+
+        //Check to see if there is anything remaining
+        changes &= ~(XOR_MASK | LINE_STYLE_MASK);
+        if (changes is 0)
+            return;
+
+        if ((changes & JOIN_MASK) !is 0)
+            gc.setLineJoin((hints & JOIN_MASK) >> JOIN_SHIFT);
+        if ((changes & CAP_MASK) !is 0)
+            gc.setLineCap((hints & CAP_MASK) >> CAP_SHIFT);
+
+        if ((changes & INTERPOLATION_MASK) !is 0)
+            gc.setInterpolation(((hints & INTERPOLATION_MASK) >> INTERPOLATION_SHIFT) - INTERPOLATION_WHOLE_NUMBER);
+
+        if ((changes & FILL_RULE_MASK) !is 0)
+            gc.setFillRule(((hints & FILL_RULE_MASK) >> FILL_RULE_SHIFT) - FILL_RULE_WHOLE_NUMBER);
+
+        if ((changes & AA_MASK) !is 0)
+            gc.setAntialias(((hints & AA_MASK) >> AA_SHIFT) - AA_WHOLE_NUMBER);
+        if ((changes & TEXT_AA_MASK) !is 0)
+            gc.setTextAntialias(((hints & TEXT_AA_MASK) >> TEXT_AA_SHIFT) - AA_WHOLE_NUMBER);
+
+        // If advanced was flagged, but none of the conditions which trigger advanced
+        // actually got applied, force advanced graphics on.
+        if ((changes & ADVANCED_GRAPHICS_MASK) !is 0) {
+            if ((hints & ADVANCED_GRAPHICS_MASK) !is 0 && !gc.getAdvanced())
+                gc.setAdvanced(true);
+        }
+    }
+}
+
+/**
+ * @see Graphics#restoreState()
+ */
+public void restoreState() {
+    restoreState(cast(State)stack.get(stackPointer - 1));
+}
+
+/**
+ * Sets all State information to that of the given State, called by restoreState()
+ * @param s the State
+ */
+protected void restoreState(State s) {
+    /*
+     * We must set the transformation matrix first since it affects things like clipping
+     * regions and patterns.
+     */
+    setAffineMatrix(s.affineMatrix);
+    currentState.relativeClip = s.relativeClip;
+    sharedClipping = true;
+
+    //If the GC is currently advanced, but it was not when pushed, revert
+    if (gc.getAdvanced() && (s.graphicHints & ADVANCED_GRAPHICS_MASK) is 0) {
+        //Set applied clip to null to force a re-setting of the clipping.
+        appliedState.relativeClip = null;
+        gc.setAdvanced(false);
+        appliedState.graphicHints &= ~ADVANCED_HINTS_MASK;
+        appliedState.graphicHints |= ADVANCED_HINTS_DEFAULTS;
+    }
+
+    setBackgroundColor(s.bgColor);
+    setBackgroundPattern(s.bgPattern);
+
+    setForegroundColor(s.fgColor);
+    setForegroundPattern(s.fgPattern);
+
+    setAlpha(s.alpha);
+    setLineWidth(s.lineWidth);
+    setFont(s.font);
+    //This method must come last because above methods will incorrectly set advanced state
+    setGraphicHints(s.graphicHints);
+
+    translateX = currentState.dx = s.dx;
+    translateY = currentState.dy = s.dy;
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#rotate(float)
+ */
+public void rotate(float degrees) {
+    //Flush clipping, patter, etc., before applying transform
+    checkGC();
+    initTransform(true);
+    transform.rotate(degrees);
+    gc.setTransform(transform);
+    elementsNeedUpdate = true;
+
+    //Can no longer operate or maintain clipping
+    appliedState.relativeClip = currentState.relativeClip = null;
+}
+
+/**
+ * @see Graphics#scale(double)
+ */
+public void scale(double factor) {
+    scale(cast(float)factor, cast(float)factor);
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see dwtx.draw2d.Graphics#scale(float, float)
+ */
+public void scale(float horizontal, float vertical) {
+    //Flush any clipping before scaling
+    checkGC();
+
+    initTransform(true);
+    transform.scale(horizontal, vertical);
+    gc.setTransform(transform);
+    elementsNeedUpdate = true;
+
+    checkSharedClipping();
+    if (currentState.relativeClip !is null)
+        currentState.relativeClip.scale(horizontal, vertical);
+}
+
+private void setAffineMatrix(float[] m) {
+    if (!elementsNeedUpdate && currentState.affineMatrix is m)
+        return;
+    currentState.affineMatrix = m;
+    if (m !is null)
+        transform.setElements(m[0], m[1], m[2], m[3], m[4], m[5]);
+    else if (transform !is null) {
+        transform.dispose();
+        transform = null;
+        elementsNeedUpdate = false;
+    }
+    gc.setTransform(transform);
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#setAlpha(int)
+ */
+public void setAlpha(int alpha) {
+    currentState.graphicHints |= ADVANCED_GRAPHICS_MASK;
+    if (currentState.alpha !is alpha)
+        gc.setAlpha(this.currentState.alpha = alpha);
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#setAntialias(int)
+ */
+public void setAntialias(int value) {
+    currentState.graphicHints &= ~AA_MASK;
+    currentState.graphicHints |= ADVANCED_GRAPHICS_MASK | (value + AA_WHOLE_NUMBER) << AA_SHIFT;
+}
+
+/**
+ * @see Graphics#setBackgroundColor(Color)
+ */
+public void setBackgroundColor(Color color) {
+    currentState.bgColor = color;
+    if (currentState.bgPattern !is null) {
+        currentState.bgPattern = null;
+        //Force the BG color to be stale
+        appliedState.bgColor = null;
+    }
+}
+
+/**
+ * @see Graphics#setBackgroundPattern(Pattern)
+ */
+public void setBackgroundPattern(Pattern pattern) {
+    currentState.graphicHints |= ADVANCED_GRAPHICS_MASK;
+    if (currentState.bgPattern is pattern)
+        return;
+    currentState.bgPattern = pattern;
+
+    if (pattern !is null)
+        initTransform(true);
+    gc.setBackgroundPattern(pattern);
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#setClip(Path)
+ */
+public void setClip(Path path) {
+    initTransform(false);
+    gc.setClipping(path);
+    appliedState.relativeClip = currentState.relativeClip = null;
+}
+
+/**
+ * @see Graphics#setClip(Rectangle)
+ */
+public void setClip(Rectangle rect) {
+    currentState.relativeClip = new RectangleClipping(rect);
+}
+
+/**
+ * @see Graphics#setFillRule(int)
+ */
+public void setFillRule(int rule) {
+    currentState.graphicHints &= ~FILL_RULE_MASK;
+    currentState.graphicHints |= (rule + FILL_RULE_WHOLE_NUMBER) << FILL_RULE_SHIFT;
+}
+
+/**
+ * @see Graphics#setFont(Font)
+ */
+public void setFont(Font f) {
+    currentState.font = f;
+}
+
+/**
+ * @see Graphics#setForegroundColor(Color)
+ */
+public void setForegroundColor(Color color) {
+    currentState.fgColor = color;
+    if (currentState.fgPattern !is null) {
+        currentState.fgPattern = null;
+        //Force fgColor to be stale
+        appliedState.fgColor = null;
+    }
+}
+
+/**
+ * @see Graphics#setForegroundPattern(Pattern)
+ */
+public void setForegroundPattern(Pattern pattern) {
+    currentState.graphicHints |= ADVANCED_GRAPHICS_MASK;
+    if (currentState.fgPattern is pattern)
+        return;
+    currentState.fgPattern = pattern;
+
+    if (pattern !is null)
+        initTransform(true);
+    gc.setForegroundPattern(pattern);
+}
+
+private void setGraphicHints(int hints) {
+    currentState.graphicHints = hints;
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#setInterpolation(int)
+ */
+public void setInterpolation(int interpolation) {
+    //values range [-1, 3]
+    currentState.graphicHints &= ~INTERPOLATION_MASK;
+    currentState.graphicHints |= ADVANCED_GRAPHICS_MASK | (interpolation + INTERPOLATION_WHOLE_NUMBER) << INTERPOLATION_SHIFT;
+}
+
+/**
+ * @see Graphics#setLineCap(int)
+ */
+public void setLineCap(int cap) {
+    currentState.graphicHints &= ~CAP_MASK;
+    currentState.graphicHints |= cap << CAP_SHIFT;
+}
+
+/**
+ * @see Graphics#setLineDash(int[])
+ */
+public void setLineDash(int[] dashes) {
+    if (dashes !is null) {
+        int copy[] = new int[dashes.length];
+        for (int i = 0; i < dashes.length; i++) {
+            int dash = dashes[i];
+            if (dash <= 0)
+                DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+            copy[i] = dash;
+        }
+        currentState.lineDash = copy;
+        setLineStyle(DWT.LINE_CUSTOM);
+        gc.setLineDash(copy);
+    } else {
+        currentState.lineDash = null;
+        setLineStyle(DWT.LINE_SOLID);
+    }
+}
+
+/**
+ * @see Graphics#setLineJoin(int)
+ */
+public void setLineJoin(int join) {
+    currentState.graphicHints &= ~JOIN_MASK;
+    currentState.graphicHints |= join << JOIN_SHIFT;
+}
+
+/**
+ * @see Graphics#setLineStyle(int)
+ */
+public void setLineStyle(int style) {
+    currentState.graphicHints &= ~LINE_STYLE_MASK;
+    currentState.graphicHints |= style;
+}
+
+/**
+ * @see Graphics#setLineWidth(int)
+ */
+public void setLineWidth(int width) {
+    currentState.lineWidth = width;
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#setTextAntialias(int)
+ */
+public void setTextAntialias(int value) {
+    currentState.graphicHints &= ~TEXT_AA_MASK;
+    currentState.graphicHints |= ADVANCED_GRAPHICS_MASK | (value + AA_WHOLE_NUMBER) << TEXT_AA_SHIFT;
+}
+
+/**
+ * @see Graphics#setXORMode(bool)
+ */
+public void setXORMode(bool xor) {
+    currentState.graphicHints &= ~XOR_MASK;
+    if (xor)
+        currentState.graphicHints |= XOR_MASK;
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#shear(float, float)
+ */
+public void shear(float horz, float vert) {
+    //Flush any clipping changes before shearing
+    checkGC();
+    initTransform(true);
+    float matrix[] = new float[6];
+    transform.getElements(matrix);
+    transform.setElements(
+            matrix[0] + matrix[2] * vert,
+            matrix[1] + matrix[3] * vert,
+            matrix[0] * horz + matrix[2],
+            matrix[1] * horz + matrix[3],
+            matrix[4],
+            matrix[5]);
+
+    gc.setTransform(transform);
+    elementsNeedUpdate = true;
+    //Can no longer track clipping changes
+    appliedState.relativeClip = currentState.relativeClip = null;
+}
+
+/**
+ * This method may require advanced graphics support if using a transform,
+ * in this case, a check should be made to ensure advanced graphics is
+ * supported in the user's environment before calling this method. See
+ * {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#translate(int, int)
+ */
+public void translate(int dx, int dy) {
+    if (dx is 0 && dy is 0)
+        return;
+    if (transform !is null) {
+        //Flush clipping, pattern, etc. before applying transform
+        checkGC();
+        transform.translate(dx, dy);
+        elementsNeedUpdate = true;
+        gc.setTransform(transform);
+    } else {
+        translateX += dx;
+        translateY += dy;
+    }
+    checkSharedClipping();
+    if (currentState.relativeClip !is null)
+        currentState.relativeClip.translate(-dx, -dy);
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#translate(float, float)
+ */
+public void translate(float dx, float dy) {
+    initTransform(true);
+    checkGC();
+    transform.translate(dx, dy);
+    elementsNeedUpdate = true;
+    gc.setTransform(transform);
+    checkSharedClipping();
+    if (currentState.relativeClip !is null)
+        currentState.relativeClip.translate(-dx, -dy);
+}
+
+private void translatePointArray(int[] points, int translateX, int translateY) {
+    if (translateX is 0 && translateY is 0)
+        return;
+    for (int i = 0; (i + 1) < points.length; i += 2) {
+        points[i] += translateX;
+        points[i + 1] += translateY;
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ScalableFigure.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ScalableFigure;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.IFigure;
+
+/**
+ * A figure that can be scaled.
+ * @author Eric Bordeau
+ * @since 2.1.1
+ */
+public interface ScalableFigure : IFigure {
+
+/**
+ * Returns the current scale.
+ * @return the current scale
+ */
+double getScale();
+
+/**
+ * Sets the new scale factor.
+ * @param scale the scale
+ */
+void setScale(double scale);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ScalableFreeformLayeredPane.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ScalableFreeformLayeredPane;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.geometry.Translatable;
+import dwtx.draw2d.FreeformLayeredPane;
+import dwtx.draw2d.ScalableFigure;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.ScaledGraphics;
+
+/**
+ * @author hudsonr
+ * @since 2.1
+ */
+public class ScalableFreeformLayeredPane
+    : FreeformLayeredPane
+    , ScalableFigure
+{
+
+private double scale = 1.0;
+
+/**
+ * @see dwtx.draw2d.Figure#getClientArea()
+ */
+public Rectangle getClientArea(Rectangle rect) {
+    super.getClientArea(rect);
+    rect.width /= scale;
+    rect.height /= scale;
+    rect.x /= scale;
+    rect.y /= scale;
+    return rect;
+}
+
+/**
+ * Returns the current zoom scale level.
+ * @return the scale
+ */
+public double getScale() {
+    return scale;
+}
+
+/**
+ * @see dwtx.draw2d.IFigure#isCoordinateSystem()
+ */
+public bool isCoordinateSystem() {
+    return true;
+}
+
+/**
+ * @see dwtx.draw2d.Figure#paintClientArea(Graphics)
+ */
+protected void paintClientArea(Graphics graphics) {
+    if (getChildren().isEmpty())
+        return;
+    if (scale is 1.0) {
+        super.paintClientArea(graphics);
+    } else {
+        ScaledGraphics g = new ScaledGraphics(graphics);
+        bool optimizeClip = getBorder() is null || getBorder().isOpaque();
+        if (!optimizeClip)
+            g.clipRect(getBounds().getCropped(getInsets()));
+        g.scale(scale);
+        g.pushState();
+        paintChildren(g);
+        g.dispose();
+        graphics.restoreState();
+    }
+}
+
+/**
+ * Sets the zoom level
+ * @param newZoom The new zoom level
+ */
+public void setScale(double newZoom) {
+    if (scale is newZoom)
+        return;
+    scale = newZoom;
+    superFireMoved(); //For AncestorListener compatibility
+    getFreeformHelper().invalidate();
+    repaint();
+}
+
+/**
+ * @see dwtx.draw2d.Figure#translateToParent(Translatable)
+ */
+public void translateToParent(Translatable t) {
+    t.performScale(scale);
+}
+
+/**
+ * @see dwtx.draw2d.Figure#translateFromParent(Translatable)
+ */
+public void translateFromParent(Translatable t) {
+    t.performScale(1 / scale);
+}
+
+/**
+ * @see dwtx.draw2d.Figure#useLocalCoordinates()
+ */
+protected final bool useLocalCoordinates() {
+    return false;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ScalableLayeredPane.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ScalableLayeredPane;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.geometry.Translatable;
+import dwtx.draw2d.LayeredPane;
+import dwtx.draw2d.ScalableFigure;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.ScaledGraphics;
+
+/**
+ * A non-freeform, scalable layered pane.
+ * @author Eric Bordeau
+ * @since 2.1.1
+ */
+public class ScalableLayeredPane
+    : LayeredPane
+    , ScalableFigure
+{
+
+private double scale = 1.0;
+
+/**
+ * @see IFigure#getClientArea(Rectangle)
+ */
+public Rectangle getClientArea(Rectangle rect) {
+    super.getClientArea(rect);
+    rect.width /= scale;
+    rect.height /= scale;
+    rect.x /= scale;
+    rect.y /= scale;
+    return rect;
+}
+
+/**
+ * @see Figure#getPreferredSize(int, int)
+ */
+public Dimension getMinimumSize(int wHint, int hHint) {
+    Dimension d = super.getMinimumSize(cast(int) (wHint / getScale()), cast(int)(hHint / getScale()));
+    int w = getInsets().getWidth();
+    int h = getInsets().getHeight();
+    return d.getExpanded(-w, -h)
+        .scale(scale)
+        .expand(w, h);
+}
+
+/**
+ * @see Figure#getPreferredSize(int, int)
+ */
+public Dimension getPreferredSize(int wHint, int hHint) {
+    Dimension d = super.getPreferredSize(cast(int) (wHint / getScale()), cast(int)(hHint / getScale()));
+    int w = getInsets().getWidth();
+    int h = getInsets().getHeight();
+    return d.getExpanded(-w, -h)
+        .scale(scale)
+        .expand(w, h);
+}
+
+/**
+ * Returns the scale level, default is 1.0.
+ * @return the scale level
+ */
+public double getScale() {
+    return scale;
+}
+
+/**
+ * @see dwtx.draw2d.IFigure#isCoordinateSystem()
+ */
+public bool isCoordinateSystem() {
+    return true;
+}
+
+/**
+ * @see dwtx.draw2d.Figure#paintClientArea(Graphics)
+ */
+protected void paintClientArea(Graphics graphics) {
+    if (getChildren().isEmpty())
+        return;
+    if (scale is 1.0) {
+        super.paintClientArea(graphics);
+    } else {
+        ScaledGraphics g = new ScaledGraphics(graphics);
+        bool optimizeClip = getBorder() is null || getBorder().isOpaque();
+        if (!optimizeClip)
+            g.clipRect(getBounds().getCropped(getInsets()));
+        g.scale(scale);
+        g.pushState();
+        paintChildren(g);
+        g.dispose();
+        graphics.restoreState();
+    }
+}
+
+/**
+ * Sets the zoom level
+ * @param newZoom The new zoom level
+ */
+public void setScale(double newZoom) {
+    if (scale is newZoom)
+        return;
+    scale = newZoom;
+    fireMoved(); //for AncestorListener compatibility
+    revalidate();
+    repaint();
+}
+
+/**
+ * @see dwtx.draw2d.Figure#translateFromParent(Translatable)
+ */
+public void translateFromParent(Translatable t) {
+    t.performScale(1 / scale);
+}
+
+/**
+ * @see dwtx.draw2d.Figure#translateToParent(Translatable)
+ */
+public void translateToParent(Translatable t) {
+    t.performScale(scale);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ScaledGraphics.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,885 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ScaledGraphics;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwt.DWT;
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.FontData;
+import dwt.graphics.FontMetrics;
+import dwt.graphics.Image;
+import dwt.graphics.Path;
+import dwt.graphics.PathData;
+import dwt.graphics.TextLayout;
+import dwt.graphics.TextStyle;
+import dwt.widgets.Display;
+static import dwt.graphics.Rectangle;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.FigureUtilities;
+
+/**
+ * A Graphics object able to scale all operations based on the current scale factor.
+ */
+public class ScaledGraphics
+    : Graphics
+{
+alias Graphics.translate translate;
+alias Graphics.fillRectangle fillRectangle;
+
+private static class FontHeightCache {
+    Font font;
+    int height;
+}
+
+static class FontKey {
+    Font font;
+    int height;
+    protected this() { }
+    protected this(Font font, int height) {
+        this.font = font;
+        this.height = height;
+    }
+
+    public override int opEquals(Object obj) {
+        return ((cast(FontKey)obj).font.opEquals(font)
+                && (cast(FontKey)obj).height is height);
+    }
+
+    public override hash_t toHash() {
+        return font.toHash() ^ height;
+    }
+
+    protected void setValues(Font font, int height) {
+        this.font = font;
+        this.height = height;
+    }
+}
+
+/**
+ * The internal state of the scaled graphics.
+ */
+protected static class State {
+    private double appliedX;
+    private double appliedY;
+    private Font font;
+    private int lineWidth;
+    private double zoom;
+
+    /**
+     * Constructs a new, uninitialized State object.
+     */
+    protected this() { }
+
+    /**
+     * Constructs a new State object and initializes the properties based on the given
+     * values.
+     *
+     * @param zoom the zoom factor
+     * @param x the x offset
+     * @param y the y offset
+     * @param font the font
+     * @param lineWidth the line width
+     */
+    protected this(double zoom, double x, double y, Font font, int lineWidth) {
+        this.zoom = zoom;
+        this.appliedX = x;
+        this.appliedY = y;
+        this.font = font;
+        this.lineWidth = lineWidth;
+    }
+
+    /**
+     * Sets all the properties of the state object.
+     * @param zoom the zoom factor
+     * @param x the x offset
+     * @param y the y offset
+     * @param font the font
+     * @param lineWidth the line width
+     */
+    protected void setValues(double zoom, double x, double y,
+                                Font font, int lineWidth) {
+        this.zoom = zoom;
+        this.appliedX = x;
+        this.appliedY = y;
+        this.font = font;
+        this.lineWidth = lineWidth;
+    }
+}
+
+private static int[][] intArrayCache;
+private final Rectangle tempRECT;
+
+static this(){
+    intArrayCache = new int[][](8);
+    for (int i = 0; i < intArrayCache.length; i++)
+        intArrayCache[i] = new int[i + 1];
+}
+private void instanceInit(){
+    tempRECT = new Rectangle();
+    localCache = new FontHeightCache();
+    targetCache = new FontHeightCache();
+    stack = new ArrayList();
+    fontKey = new FontKey();
+    fontDataCache = new HashMap();
+    fontCache = new HashMap();
+}
+private bool allowText = true;
+//private static final Point PT = new Point();
+private Map fontCache;
+private Map fontDataCache;
+private FontKey fontKey;
+private double fractionalX;
+private double fractionalY;
+private Graphics graphics;
+private FontHeightCache localCache;
+private Font localFont;
+private int localLineWidth;
+private List stack;
+private int stackPointer = 0;
+private FontHeightCache targetCache;
+
+double zoom = 1.0;
+
+/**
+ * Constructs a new ScaledGraphics based on the given Graphics object.
+ * @param g the base graphics object
+ */
+public this(Graphics g) {
+    instanceInit();
+    graphics = g;
+    localFont = g.getFont();
+    localLineWidth = g.getLineWidth();
+}
+
+/** @see Graphics#clipRect(Rectangle) */
+public void clipRect(Rectangle r) {
+    graphics.clipRect(zoomClipRect(r));
+}
+
+Font createFont(FontData data) {
+    return new Font(Display.getCurrent(), data);
+}
+
+private Path createScaledPath(Path path) {
+    PathData p = path.getPathData();
+    for (int i = 0; i < p.points.length; i += 2) {
+        p.points[i] = cast(float) (p.points[i] * zoom + fractionalX);
+        p.points[i + 1] = cast(float) (p.points[i + 1] * zoom + fractionalY);
+    }
+    Path scaledPath = new Path(path.getDevice());
+    int index = 0;
+    for (int i = 0; i < p.types.length; i++) {
+        byte type = p.types[i];
+        switch (type) {
+            case DWT.PATH_MOVE_TO:
+                scaledPath.moveTo(p.points[index], p.points[index + 1]);
+                index += 2;
+                break;
+            case DWT.PATH_LINE_TO:
+                scaledPath.lineTo(p.points[index], p.points[index + 1]);
+                index += 2;
+                break;
+            case DWT.PATH_CUBIC_TO:
+                scaledPath.cubicTo(p.points[index], p.points[index + 1],
+                        p.points[index + 2], p.points[index + 3], p.points[index + 4],
+                        p.points[index + 5]);
+                index += 6;
+                break;
+            case DWT.PATH_QUAD_TO:
+                scaledPath.quadTo(p.points[index], p.points[index + 1],
+                        p.points[index + 2], p.points[index + 3]);
+                index += 4;
+                break;
+            case DWT.PATH_CLOSE:
+                scaledPath.close();
+                break;
+        }
+    }
+    return scaledPath;
+}
+
+/** @see Graphics#dispose() */
+public void dispose() {
+    //Remove all states from the stack
+    while (stackPointer > 0) {
+        popState();
+    }
+
+    //Dispose fonts
+    Iterator iter = fontCache.values().iterator();
+    while (iter.hasNext()) {
+        Font font = (cast(Font)iter.next());
+        font.dispose();
+    }
+
+}
+
+/** @see Graphics#drawArc(int, int, int, int, int, int) */
+public void drawArc(int x, int y, int w, int h, int offset, int sweep) {
+    Rectangle z = zoomRect(x, y, w, h);
+    if (z.isEmpty() || sweep is 0)
+        return;
+    graphics.drawArc(z, offset, sweep);
+}
+
+/** @see Graphics#drawFocus(int, int, int, int) */
+public void drawFocus(int x, int y, int w, int h) {
+    graphics.drawFocus(zoomRect(x, y, w, h));
+}
+
+/** @see Graphics#drawImage(Image, int, int) */
+public void drawImage(Image srcImage, int x, int y) {
+    dwt.graphics.Rectangle.Rectangle size = srcImage.getBounds();
+    graphics.drawImage(srcImage, 0, 0, size.width, size.height,
+        cast(int)(Math.floor((x * zoom + fractionalX))),
+        cast(int)(Math.floor((y * zoom + fractionalY))),
+        cast(int)(Math.floor((size.width * zoom + fractionalX))),
+        cast(int)(Math.floor((size.height * zoom + fractionalY))));
+}
+
+/** @see Graphics#drawImage(Image, int, int, int, int, int, int, int, int) */
+public void drawImage(Image srcImage, int sx, int sy, int sw, int sh,
+                                        int tx, int ty, int tw, int th) {
+    //"t" is target rectangle, "s" = source
+
+    Rectangle t = zoomRect(tx, ty, tw, th);
+    if (!t.isEmpty())
+        graphics.drawImage(srcImage, sx, sy, sw, sh, t.x, t.y, t.width, t.height);
+}
+
+/** @see Graphics#drawLine(int, int, int, int) */
+public void drawLine(int x1, int y1, int x2, int y2) {
+    graphics.drawLine(
+        cast(int)(Math.floor((x1 * zoom + fractionalX))),
+        cast(int)(Math.floor((y1 * zoom + fractionalY))),
+        cast(int)(Math.floor((x2 * zoom + fractionalX))),
+        cast(int)(Math.floor((y2 * zoom + fractionalY))));
+}
+
+/** @see Graphics#drawOval(int, int, int, int) */
+public void drawOval(int x, int y, int w, int h) {
+    graphics.drawOval(zoomRect(x, y, w, h));
+}
+
+/** @see Graphics#drawPath(Path) */
+public void drawPath(Path path) {
+    Path scaledPath = createScaledPath(path);
+    try {
+        graphics.drawPath(scaledPath);
+    } finally {
+        scaledPath.dispose();
+    }
+}
+
+/** @see Graphics#drawPoint(int, int) */
+public void drawPoint(int x, int y) {
+    graphics.drawPoint(cast(int)Math.floor(x * zoom + fractionalX),
+            cast(int)Math.floor(y * zoom + fractionalY));
+}
+
+/**
+ * @see Graphics#drawPolygon(int[])
+ */
+public void drawPolygon(int[] points) {
+    graphics.drawPolygon(zoomPointList(points));
+}
+
+/** @see Graphics#drawPolygon(PointList) */
+public void drawPolygon(PointList points) {
+    graphics.drawPolygon(zoomPointList(points.toIntArray()));
+}
+
+/**
+ * @see Graphics#drawPolyline(int[])
+ */
+public void drawPolyline(int[] points) {
+    graphics.drawPolyline(zoomPointList(points));
+}
+
+/** @see Graphics#drawPolyline(PointList) */
+public void drawPolyline(PointList points) {
+    graphics.drawPolyline(zoomPointList(points.toIntArray()));
+}
+
+/** @see Graphics#drawRectangle(int, int, int, int) */
+public void drawRectangle(int x, int y, int w, int h) {
+    graphics.drawRectangle(zoomRect(x, y, w, h));
+}
+
+/** @see Graphics#drawRoundRectangle(Rectangle, int, int) */
+public void drawRoundRectangle(Rectangle r, int arcWidth, int arcHeight) {
+    graphics.drawRoundRectangle(zoomRect(r.x, r.y, r.width, r.height),
+        cast(int)(arcWidth * zoom),
+        cast(int)(arcHeight * zoom));
+}
+
+/** @see Graphics#drawString(String, int, int) */
+public void drawString(String s, int x, int y) {
+    if (allowText)
+        graphics.drawString(s, zoomTextPoint(x, y));
+}
+
+/** @see Graphics#drawText(String, int, int) */
+public void drawText(String s, int x, int y) {
+    if (allowText)
+        graphics.drawText(s, zoomTextPoint(x, y));
+}
+
+/**
+ * @see Graphics#drawText(String, int, int, int)
+ */
+public void drawText(String s, int x, int y, int style) {
+    if (allowText)
+        graphics.drawText(s, zoomTextPoint(x, y), style);
+}
+
+/**
+ * @see Graphics#drawTextLayout(TextLayout, int, int, int, int, Color, Color)
+ */
+public void drawTextLayout(TextLayout layout, int x, int y, int selectionStart,
+        int selectionEnd, Color selectionForeground, Color selectionBackground) {
+    TextLayout scaled = zoomTextLayout(layout);
+    graphics.drawTextLayout(scaled,
+            cast(int)Math.floor(x * zoom + fractionalX),
+            cast(int)Math.floor(y * zoom + fractionalY),
+            selectionStart, selectionEnd, selectionBackground, selectionForeground);
+    scaled.dispose();
+}
+
+/** @see Graphics#fillArc(int, int, int, int, int, int) */
+public void fillArc(int x, int y, int w, int h, int offset, int sweep) {
+    Rectangle z = zoomFillRect(x, y, w, h);
+    if (z.isEmpty() || sweep is 0)
+        return;
+    graphics.fillArc(z, offset, sweep);
+}
+
+/** @see Graphics#fillGradient(int, int, int, int, bool) */
+public void fillGradient(int x, int y, int w, int h, bool vertical) {
+    graphics.fillGradient(zoomFillRect(x, y, w, h), vertical);
+}
+
+/** @see Graphics#fillOval(int, int, int, int) */
+public void fillOval(int x, int y, int w, int h) {
+    graphics.fillOval(zoomFillRect(x, y, w, h));
+}
+
+/** @see Graphics#fillPath(Path) */
+public void fillPath(Path path) {
+    Path scaledPath = createScaledPath(path);
+    try {
+        graphics.fillPath(scaledPath);
+    } finally {
+        scaledPath.dispose();
+    }
+}
+
+/**
+ * @see Graphics#fillPolygon(int[])
+ */
+public void fillPolygon(int[] points) {
+    graphics.fillPolygon(zoomPointList(points));
+}
+
+/** @see Graphics#fillPolygon(PointList) */
+public void fillPolygon(PointList points) {
+    graphics.fillPolygon(zoomPointList(points.toIntArray()));
+}
+
+/** @see Graphics#fillRectangle(int, int, int, int) */
+public void fillRectangle(int x, int y, int w, int h) {
+    graphics.fillRectangle(zoomFillRect(x, y, w, h));
+}
+
+/** @see Graphics#fillRoundRectangle(Rectangle, int, int) */
+public void fillRoundRectangle(Rectangle r, int arcWidth, int arcHeight) {
+    graphics.fillRoundRectangle(zoomFillRect(r.x, r.y, r.width, r.height),
+        cast(int)(arcWidth * zoom),
+        cast(int)(arcHeight * zoom));
+}
+
+/** @see Graphics#fillString(String, int, int) */
+public void fillString(String s, int x, int y) {
+    if (allowText)
+        graphics.fillString(s, zoomTextPoint(x, y));
+}
+
+/** @see Graphics#fillText(String, int, int) */
+public void fillText(String s, int x, int y) {
+    if (allowText)
+        graphics.fillText(s, zoomTextPoint(x, y));
+}
+
+/**
+ * @see Graphics#getAbsoluteScale()
+ */
+public double getAbsoluteScale() {
+    return zoom * graphics.getAbsoluteScale();
+}
+
+/**
+ * @see Graphics#getAlpha()
+ */
+public int getAlpha() {
+    return graphics.getAlpha();
+}
+
+/**
+ * @see Graphics#getAntialias()
+ */
+public int getAntialias() {
+    return graphics.getAntialias();
+}
+
+/** @see Graphics#getBackgroundColor() */
+public Color getBackgroundColor() {
+    return graphics.getBackgroundColor();
+}
+
+Font getCachedFont(FontKey key) {
+    Font font = cast(Font)fontCache.get(key);
+    if (font !is null)
+        return font;
+    key = new FontKey(key.font, key.height);
+    FontData data = key.font.getFontData()[0];
+    data.setHeight(key.height);
+    Font zoomedFont = createFont(data);
+    fontCache.put(key, zoomedFont);
+    return zoomedFont;
+}
+
+FontData getCachedFontData(Font f) {
+    FontData data = cast(FontData)fontDataCache.get(f);
+    if (data !is null)
+        return data;
+    data = getLocalFont().getFontData()[0];
+    fontDataCache.put(f, data);
+    return data;
+}
+
+/** @see Graphics#getClip(Rectangle) */
+public Rectangle getClip(Rectangle rect) {
+    graphics.getClip(rect);
+    int x = cast(int)(rect.x / zoom);
+    int y = cast(int)(rect.y / zoom);
+    /*
+     * If the clip rectangle is queried, perform an inverse zoom, and take the ceiling of
+     * the resulting double. This is necessary because forward scaling essentially performs
+     * a floor() function. Without this, figures will think that they don't need to paint
+     * when actually they do.
+     */
+    rect.width = cast(int)Math.ceil(rect.right() / zoom) - x;
+    rect.height = cast(int)Math.ceil(rect.bottom() / zoom) - y;
+    rect.x = x;
+    rect.y = y;
+    return rect;
+}
+
+/**
+ * @see Graphics#getFillRule()
+ */
+public int getFillRule() {
+    return graphics.getFillRule();
+}
+
+/** @see Graphics#getFont() */
+public Font getFont() {
+    return getLocalFont();
+}
+
+/** @see Graphics#getFontMetrics() */
+public FontMetrics getFontMetrics() {
+    return FigureUtilities.getFontMetrics(localFont);
+}
+
+/** @see Graphics#getForegroundColor() */
+public Color getForegroundColor() {
+    return graphics.getForegroundColor();
+}
+
+/**
+ * @see Graphics#getInterpolation()
+ */
+public int getInterpolation() {
+    return graphics.getInterpolation();
+}
+
+/**
+ * @see Graphics#getLineCap()
+ */
+public int getLineCap() {
+    return graphics.getLineCap();
+}
+
+/**
+ * @see Graphics#getLineJoin()
+ */
+public int getLineJoin() {
+    return graphics.getLineJoin();
+}
+
+/** @see Graphics#getLineStyle() */
+public int getLineStyle() {
+    return graphics.getLineStyle();
+}
+
+/** @see Graphics#getLineWidth() */
+public int getLineWidth() {
+    return getLocalLineWidth();
+}
+
+private Font getLocalFont() {
+    return localFont;
+}
+
+private int getLocalLineWidth() {
+    return localLineWidth;
+}
+
+/**
+ * @see Graphics#getTextAntialias()
+ */
+public int getTextAntialias() {
+    return graphics.getTextAntialias();
+}
+
+/** @see Graphics#getXORMode() */
+public bool getXORMode() {
+    return graphics.getXORMode();
+}
+
+/** @see Graphics#popState() */
+public void popState() {
+    graphics.popState();
+    stackPointer--;
+    restoreLocalState(cast(State)stack.get(stackPointer));
+}
+
+/** @see Graphics#pushState() */
+public void pushState() {
+    State s;
+    if (stack.size() > stackPointer) {
+        s = cast(State)stack.get(stackPointer);
+        s.setValues(zoom, fractionalX, fractionalY, getLocalFont(), localLineWidth);
+    } else {
+        stack.add(new State(zoom, fractionalX, fractionalY, getLocalFont(),
+                                localLineWidth));
+    }
+    stackPointer++;
+
+    graphics.pushState();
+}
+
+private void restoreLocalState(State state) {
+    this.fractionalX = state.appliedX;
+    this.fractionalY = state.appliedY;
+    setScale(state.zoom);
+    setLocalFont(state.font);
+    setLocalLineWidth(state.lineWidth);
+}
+
+/** @see Graphics#restoreState() */
+public void restoreState() {
+    graphics.restoreState();
+    restoreLocalState(cast(State)stack.get(stackPointer - 1));
+}
+
+/** @see Graphics#scale(double) */
+public void scale(double amount) {
+    setScale(zoom * amount);
+}
+
+/**
+ * @see Graphics#setAlphacast(int)
+ */
+public void setAlpha(int alpha) {
+    graphics.setAlpha(alpha);
+}
+
+/**
+ * @see Graphics#setAntialiascast(int)
+ */
+public void setAntialias(int value) {
+    graphics.setAntialias(value);
+}
+
+/** @see Graphics#setBackgroundColor(Color) */
+public void setBackgroundColor(Color rgb) {
+    graphics.setBackgroundColor(rgb);
+}
+
+/** @see Graphics#setClip(Path) */
+public void setClip(Path path) {
+    Path scaledPath = createScaledPath(path);
+    try {
+        graphics.setClip(scaledPath);
+    } finally {
+        scaledPath.dispose();
+    }
+}
+
+/** @see Graphics#setClip(Rectangle) */
+public void setClip(Rectangle r) {
+    graphics.setClip(zoomClipRect(r));
+}
+
+/**
+ * @see Graphics#setFillRulecast(int)
+ */
+public void setFillRule(int rule) {
+    graphics.setFillRule(rule);
+}
+
+/** @see Graphics#setFontcast(Font) */
+public void setFont(Font f) {
+    setLocalFont(f);
+}
+
+/** @see Graphics#setForegroundColor(Color) */
+public void setForegroundColor(Color rgb) {
+    graphics.setForegroundColor(rgb);
+}
+
+/**
+ * @see dwtx.draw2d.Graphics#setInterpolationcast(int)
+ */
+public void setInterpolation(int interpolation) {
+    graphics.setInterpolation(interpolation);
+}
+
+/**
+ * @see Graphics#setLineCapcast(int)
+ */
+public void setLineCap(int cap) {
+    graphics.setLineCap(cap);
+}
+
+/**
+ * @see Graphics#setLineDash(int[])
+ */
+public void setLineDash(int[] dash) {
+    graphics.setLineDash(dash);
+}
+
+/**
+ * @see Graphics#setLineJoincast(int)
+ */
+public void setLineJoin(int join) {
+    graphics.setLineJoin(join);
+}
+
+/** @see Graphics#setLineStylecast(int) */
+public void setLineStyle(int style) {
+    graphics.setLineStyle(style);
+}
+
+/** @see Graphics#setLineWidthcast(int) */
+public void setLineWidth(int width) {
+    setLocalLineWidth(width);
+}
+
+private void setLocalFont(Font f) {
+    localFont = f;
+    graphics.setFont(zoomFont(f));
+}
+
+private void setLocalLineWidth(int width) {
+    localLineWidth = width;
+    graphics.setLineWidth(zoomLineWidth(width));
+}
+
+void setScale(double value) {
+    if (zoom is value)
+        return;
+    this.zoom = value;
+    graphics.setFont(zoomFont(getLocalFont()));
+    graphics.setLineWidth(zoomLineWidth(localLineWidth));
+}
+
+/**
+ * @see Graphics#setTextAntialiascast(int)
+ */
+public void setTextAntialias(int value) {
+    graphics.setTextAntialias(value);
+}
+
+/** @see Graphics#setXORMode(bool) */
+public void setXORMode(bool b) {
+    graphics.setXORMode(b);
+}
+
+/** @see Graphics#translate(int, int) */
+public void translate(int dx, int dy) {
+    // fractionalX/Y is the fractional part left over from previous
+    // translates that gets lost in the integer approximation.
+    double dxFloat = dx * zoom + fractionalX;
+    double dyFloat = dy * zoom + fractionalY;
+    fractionalX = dxFloat - Math.floor(dxFloat);
+    fractionalY = dyFloat - Math.floor(dyFloat);
+    graphics.translate(cast(int)Math.floor(dxFloat), cast(int)Math.floor(dyFloat));
+}
+
+/** @see Graphics#translate(float, float) */
+public void translate(float dx, float dy) {
+    double dxFloat = dx * zoom + fractionalX;
+    double dyFloat = dy * zoom + fractionalY;
+    fractionalX = dxFloat - Math.floor(dxFloat);
+    fractionalY = dyFloat - Math.floor(dyFloat);
+    graphics.translate(cast(int)Math.floor(dxFloat), cast(int)Math.floor(dyFloat));
+}
+
+private Rectangle zoomClipRect(Rectangle r) {
+    tempRECT.x = cast(int)(Math.floor(r.x * zoom + fractionalX));
+    tempRECT.y = cast(int)(Math.floor(r.y * zoom + fractionalY));
+    tempRECT.width = cast(int)(Math.ceil(((r.x + r.width) * zoom + fractionalX))) - tempRECT.x;
+    tempRECT.height = cast(int)(Math.ceil(((r.y + r.height) * zoom + fractionalY))) - tempRECT.y;
+    return tempRECT;
+}
+
+private Rectangle zoomFillRect(int x, int y, int w, int h) {
+    tempRECT.x = cast(int)(Math.floor((x * zoom + fractionalX)));
+    tempRECT.y = cast(int)(Math.floor((y * zoom + fractionalY)));
+    tempRECT.width = cast(int)(Math.floor(((x + w - 1) * zoom + fractionalX))) - tempRECT.x + 1;
+    tempRECT.height = cast(int)(Math.floor(((y + h - 1) * zoom + fractionalY))) - tempRECT.y + 1;
+    return tempRECT;
+}
+
+Font zoomFont(Font f) {
+    if (f is null)
+        f = Display.getCurrent().getSystemFont();
+    FontData data = getCachedFontData(f);
+    int zoomedFontHeight = zoomFontHeight(data.getHeight());
+    allowText = zoomedFontHeight > 0;
+    fontKey.setValues(f, zoomedFontHeight);
+    return getCachedFont(fontKey);
+}
+
+int zoomFontHeight(int height) {
+    return cast(int)(zoom * height);
+}
+
+int zoomLineWidth(int w) {
+    return w;
+}
+
+private int[] zoomPointList(int[] points) {
+    int[] scaled = null;
+
+    // Look in cache for a integer array with the same length as 'points'
+    for (int i = 0; i < intArrayCache.length; i++) {
+        if (intArrayCache[i].length is points.length) {
+            scaled = intArrayCache[i];
+
+            // Move this integer array up one notch in the array
+            if (i !is 0) {
+                int[] temp = intArrayCache[i - 1];
+                intArrayCache[i - 1] = scaled;
+                intArrayCache[i] = temp;
+            }
+        }
+    }
+
+    // If no match is found, take the one that is last and resize it.
+    if (scaled is null) {
+        intArrayCache[intArrayCache.length - 1] = new int[points.length];
+        scaled = intArrayCache[intArrayCache.length - 1];
+    }
+
+    // Scale the points
+    for (int i = 0; (i + 1) < points.length; i += 2) {
+        scaled[i] = cast(int)(Math.floor((points[i] * zoom + fractionalX)));
+        scaled[i + 1] = cast(int)(Math.floor((points[i + 1] * zoom + fractionalY)));
+    }
+    return scaled;
+}
+
+private Rectangle zoomRect(int x, int y, int w, int h) {
+    tempRECT.x = cast(int)(Math.floor(x * zoom + fractionalX));
+    tempRECT.y = cast(int)(Math.floor(y * zoom + fractionalY));
+    tempRECT.width = cast(int)(Math.floor(((x + w) * zoom + fractionalX))) - tempRECT.x;
+    tempRECT.height = cast(int)(Math.floor(((y + h) * zoom + fractionalY))) - tempRECT.y;
+    return tempRECT;
+}
+
+private TextLayout zoomTextLayout(TextLayout layout) {
+    TextLayout zoomed = new TextLayout(Display.getCurrent());
+    zoomed.setText(layout.getText());
+
+    int zoomWidth = -1;
+
+    if (layout.getWidth() !is -1)
+        zoomWidth = (cast(int)(layout.getWidth() * zoom));
+
+    if (zoomWidth < -1 || zoomWidth is 0)
+        return null;
+
+    zoomed.setFont(zoomFont(layout.getFont()));
+    zoomed.setAlignment(layout.getAlignment());
+    zoomed.setAscent(layout.getAscent());
+    zoomed.setDescent(layout.getDescent());
+    zoomed.setOrientation(layout.getOrientation());
+    zoomed.setSegments(layout.getSegments());
+    zoomed.setSpacing(layout.getSpacing());
+    zoomed.setTabs(layout.getTabs());
+
+    zoomed.setWidth(zoomWidth);
+    int length = layout.getText().length;
+    if (length > 0) {
+        int start = 0, offset = 1;
+        TextStyle style = null, lastStyle = layout.getStyle(0);
+        for (; offset <= length; offset++) {
+            if (offset !is length
+                    && (style = layout.getStyle(offset)) is lastStyle)
+                continue;
+            int end = offset - 1;
+
+            if (lastStyle !is null) {
+                TextStyle zoomedStyle = new TextStyle(zoomFont(lastStyle.font),
+                        lastStyle.foreground, lastStyle.background);
+                zoomedStyle.metrics = lastStyle.metrics;
+                zoomedStyle.rise = lastStyle.rise;
+                zoomedStyle.strikeout = lastStyle.strikeout;
+                zoomedStyle.underline = lastStyle.underline;
+                zoomed.setStyle(zoomedStyle, start, end);
+            }
+            lastStyle = style;
+            start = offset;
+        }
+    }
+    return zoomed;
+}
+
+private Point zoomTextPoint(int x, int y) {
+    if (localCache.font !is localFont) {
+        //Font is different, re-calculate its height
+        FontMetrics metric = FigureUtilities.getFontMetrics(localFont);
+        localCache.height = metric.getHeight() - metric.getDescent();
+        localCache.font = localFont;
+    }
+    if (targetCache.font !is graphics.getFont()) {
+        FontMetrics metric = graphics.getFontMetrics();
+        targetCache.font = graphics.getFont();
+        targetCache.height = metric.getHeight() - metric.getDescent();
+    }
+    return new Point((cast(int)(Math.floor((x * zoom) + fractionalX))),
+                        cast(int)(Math.floor((y + localCache.height - 1) * zoom
+                                            - targetCache.height + 1 + fractionalY)));
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/SchemeBorder.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,336 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.SchemeBorder;
+
+import dwt.dwthelper.utils;
+
+
+
+import dwt.graphics.Color;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Border;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.AbstractBorder;
+import dwtx.draw2d.ColorConstants;
+
+/**
+ * SchemeBorder allows the creation of borders based on
+ * {@link SchemeBorder.Scheme Schemes}. A <i>Scheme</i> is a class whose only purpose is
+ * to carry border specific information. SchemeBorder renders the border based on the
+ * information given by the <i>Scheme</i> set to it.
+ */
+public class SchemeBorder
+    : AbstractBorder
+    //, ColorConstants
+{
+
+/** The {@link SchemeBorder.Scheme} associated with this SchemeBorder **/
+protected Scheme scheme = null;
+
+/** Arrays of Colors, used for shadow or highlight effects **/
+protected static const Color[]
+    DARKEST_DARKER,
+    LIGHTER_DARKER,
+    DARKER_LIGHTER;
+
+static this(){
+    DARKEST_DARKER   = [ColorConstants.buttonDarkest,  ColorConstants.buttonDarker];
+    LIGHTER_DARKER   = [ColorConstants.buttonLightest, ColorConstants.buttonDarker];
+    DARKER_LIGHTER   = [ColorConstants.buttonDarker, ColorConstants.buttonLightest];
+}
+
+/**
+ * Holds a set of information about a border, which can be changed to create a wide range
+ * of schemes. Provides support for border opacity, size, highlight side and shadow side
+ * colors.
+ */
+public static class Scheme {
+    private Insets insets;
+    private bool isOpaque_ = false;
+
+    /** Arrays of Colors, used for highlight and shadow effecsts */
+    protected Color[]
+        highlight,
+        shadow;
+
+    /**
+     * Constructs a default border Scheme with no border sides.
+     * @since 2.0
+     */
+    protected this() { }
+
+    /**
+     * Constructs a border Scheme with the specified highlight and shadow colors. The size
+     * of the border depends on the number of colors passed in for each parameter.
+     * Hightlight colors are used in the top and left sides of the border, and Shadow
+     * colors are used in the bottom and right sides of the border.
+     *
+     * @param highlight the hightlight colors
+     * @param shadow the shadow colors
+     * @since 2.0
+     */
+    public this(Color[] highlight, Color[] shadow) {
+        this.highlight = highlight;
+        this.shadow = shadow;
+        init();
+    }
+
+    /**
+     * Constructs a border scheme with the specified colors. The input colors serve as
+     * both highlight and shadow colors. The size of the border is the number of colors
+     * passed in as input. Hightlight colors are used in the top and left sides of the
+     * border, and Shadow colors are used in the bottom and right sides of the border.
+     *
+     * @param colors the colors to be used for the border
+     * @since 2.0
+     */
+    public this(Color[] colors) {
+        highlight = shadow = colors;
+        init();
+    }
+
+    /**
+     * Calculates and returns the Insets for this border Scheme. The calculations depend
+     * on the number of colors passed in as input.
+     *
+     * @return the Insets used by this border
+     * @since 2.0
+     */
+    protected Insets calculateInsets() {
+        int tl = getHighlight().length;
+        int br = getShadow().length;
+        return new Insets(tl, tl, br, br);
+    }
+
+    /**
+     * Calculates and retuns the opaque state of this border scheme. Returns
+     * <code>false</code> if any of the border colors are <code>null</code>. This is done
+     * to prevent the appearance of underlying pixels since the border color is
+     * <code>null</code>.
+     *
+     * @return <code>true</code> if this border is opaque
+     * @since 2.0
+     */
+    protected bool calculateOpaque() {
+        Color colors [] = getHighlight();
+        for (int i = 0; i < colors.length; i++)
+            if (colors[i] is null)
+                return false;
+        colors = getShadow();
+        for (int i = 0; i < colors.length; i++)
+            if (colors[i] is null)
+                return false;
+        return true;
+    }
+
+    /**
+     * Returns the highlight colors of this border scheme as an array of Colors.
+     *
+     * @return the highlight colors
+     * @since 2.0
+     */
+    protected Color[] getHighlight() {
+        return highlight;
+    }
+
+    /**
+     * Returns the Insets required by this Scheme.
+     *
+     * @return the Insets
+     * @since 2.0
+     */
+    protected Insets getInsets() {
+        return insets;
+    }
+
+    /**
+     * Returns the shadow colors of this border scheme as an array of Colors.
+     *
+     * @return the shadow colors
+     * @since 2.0
+     */
+    protected Color[] getShadow() {
+        return shadow;
+    }
+
+    /**
+     * Calculates and initializes the properties of this border scheme.
+     *
+     * @since 2.0
+     */
+    protected void init() {
+        insets = calculateInsets();
+        isOpaque_ = calculateOpaque();
+    }
+
+    /**
+     * Returns whether this border should be opaque or not.
+     *
+     * @return <code>true</code> if this border is opaque
+     * @since 2.0
+     */
+    protected bool isOpaque() {
+        return isOpaque_;
+    }
+}
+
+/**
+ * Interface which defines some commonly used schemes for the border. These schemes can be
+ * given as input to the {@link SchemeBorder SchemeBorder} to generate appropriate borders.
+ */
+public struct SCHEMES {
+
+    /** Schemes used for shadow and highlight effects **/
+    public static Scheme
+        BUTTON_CONTRAST,
+        BUTTON_RAISED,
+        BUTTON_PRESSED,
+        RAISED,
+        LOWERED,
+        RIDGED,
+        ETCHED;
+
+    static this(){
+        BUTTON_CONTRAST = new Scheme(
+            [ColorConstants.button, ColorConstants.buttonLightest],
+            DARKEST_DARKER
+        );
+        BUTTON_RAISED = new Scheme(
+            [ColorConstants.buttonLightest],
+            DARKEST_DARKER
+        );
+        BUTTON_PRESSED = new Scheme(
+            DARKEST_DARKER,
+            [ColorConstants.buttonLightest]
+        );
+        RAISED = new Scheme(
+            [ColorConstants.buttonLightest],
+            [ColorConstants.buttonDarkest]
+        );
+        LOWERED = new Scheme(
+            [ColorConstants.buttonDarkest],
+            [ColorConstants.buttonLightest]
+        );
+        RIDGED = new Scheme(LIGHTER_DARKER, DARKER_LIGHTER);
+        ETCHED = new Scheme(DARKER_LIGHTER, LIGHTER_DARKER);
+    }
+}
+
+/**
+ * Constructs a default SchemeBorder with no scheme defined.
+ * @since 2.0
+ */
+protected this() { }
+
+/**
+ * Constructs a SchemeBorder with the Scheme given as input.
+ *
+ * @param scheme the Scheme to be used by this border
+ * @since 2.0
+ */
+public this(Scheme scheme) {
+    setScheme(scheme);
+}
+
+/**
+ * @see Border#getInsets(IFigure)
+ */
+public Insets getInsets(IFigure figure) {
+    return getScheme().getInsets();
+}
+
+/**
+ * Returns the scheme used by this border.
+ *
+ * @return the Scheme used by this border
+ * @since 2.0
+ */
+protected Scheme getScheme() {
+    return scheme;
+}
+
+/**
+ * Returns the opaque state of this border. Returns <code>true</code> indicating that this
+ * will fill in the area enclosed by the border.
+ *
+ * @see Border#isOpaque()
+ */
+public bool isOpaque() {
+    return true;
+}
+
+/**
+ * Sets the Scheme for this border to the Scheme given as input.
+ *
+ * @param scheme the Scheme for this border
+ * @since 2.0
+ */
+protected void setScheme(Scheme scheme) {
+    this.scheme = scheme;
+}
+
+/**
+ * @see Border#paint(IFigure, Graphics, Insets)
+ */
+public void paint(IFigure figure, Graphics g, Insets insets) {
+    Color [] tl = scheme.getHighlight();
+    Color [] br = scheme.getShadow();
+
+    paint (g, figure, insets, tl, br);
+}
+
+/**
+ * Paints the border using the information in the set Scheme and the inputs given. Side
+ * widths are determined by the number of colors in the Scheme for each side.
+ *
+ * @param graphics the graphics object
+ * @param fig the figure this border belongs to
+ * @param insets the insets
+ * @param tl the highlight (top/left) colors
+ * @param br the shadow (bottom/right) colors
+ */
+protected void paint(Graphics graphics, IFigure fig,
+                        Insets insets, Color[] tl, Color[] br) {
+    graphics.setLineWidth(1);
+    graphics.setLineStyle(Graphics.LINE_SOLID);
+    graphics.setXORMode(false);
+
+    Rectangle rect = getPaintRectangle(fig, insets);
+
+    int top    = rect.y;
+    int left   = rect.x;
+    int bottom = rect.bottom() - 1;
+    int right  = rect.right() - 1;
+    Color color;
+
+    for (int i = 0; i < br.length; i++) {
+        color = br[i];
+        graphics.setForegroundColor(color);
+        graphics.drawLine(right - i, bottom - i, right - i, top + i);
+        graphics.drawLine(right - i, bottom - i, left + i, bottom - i);
+    }
+
+    right--;
+    bottom--;
+
+    for (int i = 0; i < tl.length; i++) {
+        color = tl[i];
+        graphics.setForegroundColor(color);
+        graphics.drawLine(left + i, top + i, right - i, top + i);
+        graphics.drawLine(left + i, top + i, left + i, bottom - i);
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ScrollBar.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,667 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ScrollBar;
+
+import dwt.dwthelper.utils;
+
+import dwtx.dwtxhelper.Bean;
+
+import dwt.graphics.Color;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.geometry.Transposer;
+
+import dwtx.draw2d.Orientable;
+import dwtx.draw2d.Figure;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.RangeModel;
+import dwtx.draw2d.Clickable;
+import dwtx.draw2d.MouseMotionListener;
+import dwtx.draw2d.MouseListener;
+import dwtx.draw2d.MouseEvent;
+import dwtx.draw2d.FigureUtilities;
+import dwtx.draw2d.ColorConstants;
+import dwtx.draw2d.Button;
+import dwtx.draw2d.RangeModel;
+import dwtx.draw2d.DefaultRangeModel;
+import dwtx.draw2d.ArrowButton;
+import dwtx.draw2d.ButtonBorder;
+import dwtx.draw2d.ChangeListener;
+import dwtx.draw2d.ChangeEvent;
+import dwtx.draw2d.Panel;
+import dwtx.draw2d.ScrollBarLayout;
+import dwtx.draw2d.SchemeBorder;
+import dwtx.draw2d.ActionListener;
+import dwtx.draw2d.ActionEvent;
+
+/**
+ * Provides for the scrollbars used by the {@link ScrollPane}. A ScrollBar is made up of
+ * five essential Figures: An 'Up' arrow button, a 'Down' arrow button, a draggable
+ * 'Thumb', a 'Pageup' button, and a 'Pagedown' button.
+ */
+public class ScrollBar
+    : Figure
+    , Orientable, PropertyChangeListener
+{
+
+private static const int ORIENTATION_FLAG = Figure.MAX_FLAG << 1;
+/** @see Figure#MAX_FLAG */
+protected static const int MAX_FLAG = ORIENTATION_FLAG;
+
+private static const Color COLOR_TRACK;
+static this(){
+    COLOR_TRACK = FigureUtilities.mixColors(
+            ColorConstants.white,
+            ColorConstants.button);
+}
+
+private RangeModel rangeModel = null;
+private IFigure thumb;
+private Clickable pageUp_, pageDown_;
+private Clickable buttonUp, buttonDown;
+/**
+ * Listens to mouse events on the scrollbar to take care of scrolling.
+ */
+protected ThumbDragger thumbDragger;
+
+private bool isHorizontal_ = false;
+
+private int pageIncrement = 50;
+private int stepIncrement = 10;
+
+/**
+ * Transposes from vertical to horizontal if needed.
+ */
+protected /+final+/ Transposer transposer;
+
+private void instanceInit(){
+    thumbDragger = new ThumbDragger();
+    transposer = new Transposer();
+    setRangeModel(new DefaultRangeModel());
+}
+
+/**
+ * Constructs a ScrollBar. ScrollBar orientation is vertical by default. Call
+ * {@link #setHorizontal(bool)} with <code>true</code> to set horizontal orientation.
+ *
+ * @since 2.0
+ */
+public this() {
+    instanceInit();
+    initialize();
+}
+
+/**
+ * Creates the default 'Up' ArrowButton for the ScrollBar.
+ *
+ * @return the up button
+ * @since 2.0
+ */
+protected Clickable createDefaultUpButton() {
+    Button buttonUp = new ArrowButton();
+    buttonUp.setBorder(new ButtonBorder(ButtonBorder.SCHEMES.BUTTON_SCROLLBAR));
+    return buttonUp;
+}
+
+/**
+ * Creates the default 'Down' ArrowButton for the ScrollBar.
+ *
+ * @return the down button
+ * @since 2.0
+ */
+protected Clickable createDefaultDownButton() {
+    Button buttonDown = new ArrowButton();
+    buttonDown.setBorder(new ButtonBorder(ButtonBorder.SCHEMES.BUTTON_SCROLLBAR));
+    return buttonDown;
+}
+
+/**
+ * Creates the pagedown Figure for the Scrollbar.
+ *
+ * @return the page down figure
+ * @since 2.0
+ */
+protected Clickable createPageDown() {
+    return createPageUp();
+}
+
+/**
+ * Creates the pageup Figure for the Scrollbar.
+ *
+ * @return the page up figure
+ * @since 2.0
+ */
+protected Clickable createPageUp() {
+    Clickable clickable = new Clickable();
+    clickable.setOpaque(true);
+    clickable.setBackgroundColor(COLOR_TRACK);
+    clickable.setRequestFocusEnabled(false);
+    clickable.setFocusTraversable(false);
+    clickable.addChangeListener( dgChangeListener( (ChangeEvent evt, Clickable clickable_){
+        if (clickable_.getModel().isArmed())
+            clickable_.setBackgroundColor(ColorConstants.black);
+        else
+            clickable_.setBackgroundColor(COLOR_TRACK);
+    }, clickable));
+    return clickable;
+}
+
+/**
+ * Creates the Scrollbar's "thumb", the draggable Figure that indicates the Scrollbar's
+ * position.
+ *
+ * @return the thumb figure
+ * @since 2.0
+ */
+protected IFigure createDefaultThumb() {
+    Panel thumb = new Panel();
+    thumb.setMinimumSize(new Dimension(6, 6));
+    thumb.setBackgroundColor(ColorConstants.button);
+
+    thumb.setBorder(new SchemeBorder(SchemeBorder.SCHEMES.BUTTON_CONTRAST));
+    return thumb;
+}
+
+/**
+ * Returns the figure used as the up button.
+ * @return the up button
+ */
+protected IFigure getButtonUp() {
+    // TODO: The set method takes a Clickable while the get method returns an IFigure.
+    // Change the get method to return Clickable (since that's what it's typed as).
+    return buttonUp;
+}
+
+/**
+ * Returns the figure used as the down button.
+ * @return the down button
+ */
+protected IFigure getButtonDown() {
+    // TODO: The set method takes a Clickable while the get method returns an IFigure.
+    // Change the get method to return Clickable (since that's what it's typed as).
+    return buttonDown;
+}
+
+/**
+ * Returns the extent.
+ * @return the extent
+ * @see RangeModel#getExtent()
+ */
+public int getExtent() {
+    return getRangeModel().getExtent();
+}
+
+/**
+ * Returns the minumum value.
+ * @return the minimum
+ * @see RangeModel#getMinimum()
+ */
+public int getMinimum() {
+    return getRangeModel().getMinimum();
+}
+
+/**
+ * Returns the maximum value.
+ * @return the maximum
+ * @see RangeModel#getMaximum()
+ */
+public int getMaximum() {
+    return getRangeModel().getMaximum();
+}
+
+/**
+ * Returns the figure used for page down.
+ * @return the page down figure
+ */
+protected IFigure getPageDown() {
+    // TODO: The set method takes a Clickable while the get method returns an IFigure.
+    // Change the get method to return Clickable (since that's what it's typed as).
+    return pageDown_;
+}
+
+/**
+ * Returns the the amound the scrollbar will move when the page up or page down areas are
+ * pressed.
+ * @return the page increment
+ */
+public int getPageIncrement() {
+    return pageIncrement;
+}
+
+/**
+ * Returns the figure used for page up.
+ * @return the page up figure
+ */
+protected IFigure getPageUp() {
+    // TODO: The set method takes a Clickable while the get method returns an IFigure.
+    // Change the get method to return Clickable (since that's what it's typed as).
+    return pageUp_;
+}
+
+/**
+ * Returns the range model for this scrollbar.
+ * @return the range model
+ */
+public RangeModel getRangeModel() {
+    return rangeModel;
+}
+
+/**
+ * Returns the amount the scrollbar will move when the up or down arrow buttons are
+ * pressed.
+ * @return the step increment
+ */
+public int getStepIncrement() {
+    return stepIncrement;
+}
+
+/**
+ * Returns the figure used as the scrollbar's thumb.
+ * @return the thumb figure
+ */
+protected IFigure getThumb() {
+    return thumb;
+}
+
+/**
+ * Returns the current scroll position of the scrollbar.
+ * @return the current value
+ * @see RangeModel#getValue()
+ */
+public int getValue() {
+    return getRangeModel().getValue();
+}
+
+/**
+ * Returns the size of the range of allowable values.
+ * @return the value range
+ */
+protected int getValueRange() {
+    return getMaximum() - getExtent() - getMinimum();
+}
+
+/**
+ * Initilization of the ScrollBar. Sets the Scrollbar to have a ScrollBarLayout with
+ * vertical orientation. Creates the Figures that make up the components of the ScrollBar.
+ *
+ * @since 2.0
+ */
+protected void initialize() {
+    setLayoutManager(new ScrollBarLayout(transposer));
+    setUpClickable(createDefaultUpButton());
+    setDownClickable(createDefaultDownButton());
+    setPageUp(createPageUp());
+    setPageDown(createPageDown());
+    setThumb(createDefaultThumb());
+}
+
+/**
+ * Returns <code>true</code> if this scrollbar is orientated horizontally,
+ * <code>false</code> otherwise.
+ * @return whether this scrollbar is horizontal
+ */
+public bool isHorizontal() {
+    return isHorizontal_;
+}
+
+private void pageDown() {
+    setValue(getValue() + getPageIncrement());
+}
+
+private void pageUp() {
+    setValue(getValue() - getPageIncrement());
+}
+
+/**
+ * @see PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
+ */
+public void propertyChange(PropertyChangeEvent event) {
+    if (null !is cast(RangeModel)event.getSource() ) {
+        setEnabled(getRangeModel().isEnabled());
+        if (RangeModel.PROPERTY_VALUE.equals(event.getPropertyName())) {
+            firePropertyChange("value", event.getOldValue(), //$NON-NLS-1$
+                                        event.getNewValue());
+            revalidate();
+        }
+        if (RangeModel.PROPERTY_MINIMUM.equals(event.getPropertyName())) {
+            firePropertyChange("value", event.getOldValue(), //$NON-NLS-1$
+                                        event.getNewValue());
+            revalidate();
+        }
+        if (RangeModel.PROPERTY_MAXIMUM.equals(event.getPropertyName())) {
+            firePropertyChange("value", event.getOldValue(), //$NON-NLS-1$
+                                        event.getNewValue());
+            revalidate();
+        }
+        if (RangeModel.PROPERTY_EXTENT.equals(event.getPropertyName())) {
+            firePropertyChange("value", event.getOldValue(), //$NON-NLS-1$
+                                        event.getNewValue());
+            revalidate();
+        }
+    }
+}
+
+/**
+ * @see IFigure#revalidate()
+ */
+public void revalidate() {
+    // Override default revalidate to prevent going up the parent chain. Reason for this
+    // is that preferred size never changes unless orientation changes.
+    invalidate();
+    getUpdateManager().addInvalidFigure(this);
+}
+
+/**
+ * Does nothing because this doesn't make sense for a scrollbar.
+ * @see Orientable#setDirection(int)
+ */
+public void setDirection(int direction) {
+    //Doesn't make sense for Scrollbar.
+}
+
+/**
+ * Sets the Clickable that represents the down arrow of the Scrollbar to <i>down</i>.
+ *
+ * @param down the down button
+ * @since 2.0
+ */
+public void setDownClickable(Clickable down) {
+    if (buttonDown !is null) {
+        remove(buttonDown);
+    }
+    buttonDown = down;
+    if (buttonDown !is null) {
+        if (auto b = cast(Orientable)buttonDown )
+            b.setDirection(isHorizontal()
+                                                    ? Orientable.EAST
+                                                    : Orientable.SOUTH);
+        buttonDown.setFiringMethod(Clickable.REPEAT_FIRING);
+        buttonDown.addActionListener(dgActionListener( (ActionEvent evt){
+            stepDown();
+        }));
+        add(buttonDown, stringcast(ScrollBarLayout.DOWN_ARROW));
+    }
+}
+
+/**
+ * Sets the Clickable that represents the up arrow of the Scrollbar to <i>up</i>.
+ *
+ * @param up the up button
+ * @since 2.0
+ */
+public void setUpClickable(Clickable up) {
+    if (buttonUp !is null) {
+        remove(buttonUp);
+    }
+    buttonUp = up;
+    if (up !is null) {
+        if (auto o = cast(Orientable)up )
+            o.setDirection(isHorizontal()
+                                            ? Orientable.WEST
+                                            : Orientable.NORTH);
+        buttonUp.setFiringMethod(Clickable.REPEAT_FIRING);
+        buttonUp.addActionListener(dgActionListener( (ActionEvent evt){
+            stepUp();
+        }));
+        add(buttonUp, stringcast(ScrollBarLayout.UP_ARROW));
+    }
+}
+
+/**
+ * @see IFigure#setEnabled(bool)
+ */
+public void setEnabled(bool value) {
+    if (isEnabled() is value)
+        return;
+    super.setEnabled(value);
+    setChildrenEnabled(value);
+    if (getThumb() !is null) {
+        getThumb().setVisible(value);
+        revalidate();
+    }
+}
+
+/**
+ * Sets the extent of the Scrollbar to <i>ext</i>
+ *
+ * @param ext the extent
+ * @since 2.0
+ */
+public void setExtent(int ext) {
+    if (getExtent() is ext)
+        return;
+    getRangeModel().setExtent(ext);
+}
+
+/**
+ * Sets the orientation of the ScrollBar. If <code>true</code>, the Scrollbar will have
+ * a horizontal orientation. If <code>false</code>, the scrollBar will have a vertical
+ * orientation.
+ *
+ * @param value <code>true</code> if the scrollbar should be horizontal
+ * @since 2.0
+ */
+public final void setHorizontal(bool value) {
+    setOrientation(value ? HORIZONTAL : VERTICAL);
+}
+
+/**
+ * Sets the maximum position to <i>max</i>.
+ *
+ * @param max the maximum position
+ * @since 2.0
+ */
+public void setMaximum(int max) {
+    if (getMaximum() is max)
+        return;
+    getRangeModel().setMaximum(max);
+}
+
+/**
+ * Sets the minimum position to <i>min</i>.
+ *
+ * @param min the minumum position
+ * @since 2.0
+ */
+public void setMinimum(int min) {
+    if (getMinimum() is min)
+        return;
+    getRangeModel().setMinimum(min);
+}
+
+/**
+ * @see Orientable#setOrientation(int)
+ */
+public void setOrientation(int value) {
+    if ((value is HORIZONTAL) is isHorizontal())
+        return;
+    isHorizontal_ = value is HORIZONTAL;
+    transposer.setEnabled(isHorizontal_);
+
+    setChildrenOrientation(value);
+    super.revalidate();
+}
+
+/**
+ * Sets the ScrollBar to scroll <i>increment</i> pixels when its pageup or pagedown
+ * buttons are pressed. (Note that the pageup and pagedown buttons are <b>NOT</b> the
+ * arrow buttons, they are the figures between the arrow buttons and the ScrollBar's
+ * thumb figure).
+ *
+ * @param increment the new page increment
+ * @since 2.0
+ */
+public void setPageIncrement(int increment) {
+    pageIncrement = increment;
+}
+
+/**
+ * Sets the pagedown button to the passed Clickable. The pagedown button is the figure
+ * between the down arrow button and the ScrollBar's thumb figure.
+ *
+ * @param down the page down figure
+ * @since 2.0
+ */
+public void setPageDown(Clickable down) {
+    if (pageDown_ !is null)
+        remove(pageDown_);
+    pageDown_ = down;
+    if (pageDown_ !is null) {
+        pageDown_.setFiringMethod(Clickable.REPEAT_FIRING);
+        pageDown_.addActionListener(dgActionListener( (ActionEvent evt){
+            pageDown();
+        }));
+        add(down,stringcast( ScrollBarLayout.PAGE_DOWN));
+    }
+}
+
+/**
+ * Sets the pageup button to the passed Clickable. The pageup button is the rectangular
+ * figure between the down arrow button and the ScrollBar's thumb figure.
+ *
+ * @param up the page up figure
+ * @since 2.0
+ */
+public void setPageUp(Clickable up) {
+    if (pageUp_ !is null)
+        remove(pageUp_);
+    pageUp_ = up;
+    if (pageUp_ !is null) {
+        pageUp_.setFiringMethod(Clickable.REPEAT_FIRING);
+        pageUp_.addActionListener(dgActionListener((ActionEvent evt){
+            pageUp();
+        }));
+        add(pageUp_, stringcast(ScrollBarLayout.PAGE_UP));
+    }
+}
+
+/**
+ * Sets the ScrollBar's RangeModel to the passed value.
+ *
+ * @param rangeModel the new range model
+ * @since 2.0
+ */
+public void setRangeModel(RangeModel rangeModel) {
+    if (this.rangeModel !is null)
+        this.rangeModel.removePropertyChangeListener(this);
+    this.rangeModel = rangeModel;
+    rangeModel.addPropertyChangeListener(this);
+}
+
+/**
+ * Sets the ScrollBar's step increment to the passed value. The step increment indicates
+ * how many pixels the ScrollBar will scroll when its up or down arrow button is pressed.
+ *
+ * @param increment the new step increment
+ * @since 2.0
+ */
+public void setStepIncrement(int increment) {
+    stepIncrement = increment;
+}
+
+/**
+ * Sets the ScrollBar's thumb to the passed Figure. The thumb is the draggable component
+ * of the ScrollBar that indicates the ScrollBar's position.
+ *
+ * @param figure the thumb figure
+ * @since 2.0
+ */
+public void setThumb(IFigure figure) {
+    if (thumb !is null) {
+        thumb.removeMouseListener(thumbDragger);
+        thumb.removeMouseMotionListener(thumbDragger);
+        remove(thumb);
+    }
+    thumb = figure;
+    if (thumb !is null) {
+        thumb.addMouseListener(thumbDragger);
+        thumb.addMouseMotionListener(thumbDragger);
+        add(thumb, stringcast(ScrollBarLayout.THUMB));
+    }
+}
+
+/**
+ * Sets the value of the Scrollbar to <i>v</i>
+ *
+ * @param v the new value
+ * @since 2.0
+ */
+public void setValue(int v) {
+    getRangeModel().setValue(v);
+}
+
+/**
+ * Causes the ScrollBar to scroll down (or right) by the value of its step increment.
+ *
+ * @since 2.0
+ */
+protected void stepDown() {
+    setValue(getValue() + getStepIncrement());
+}
+
+/**
+ * Causes the ScrollBar to scroll up (or left) by the value of its step increment.
+ *
+ * @since 2.0
+ */
+protected void stepUp() {
+    setValue(getValue() - getStepIncrement());
+}
+
+class ThumbDragger
+    : MouseMotionListener.Stub
+    , MouseListener
+{
+    protected Point start;
+    protected int dragRange;
+    protected int revertValue;
+    protected bool armed;
+    public this() { }
+
+    public void mousePressed(MouseEvent me) {
+        armed = true;
+        start = me.getLocation();
+        Rectangle area = new Rectangle(transposer.t(getClientArea()));
+        Dimension thumbSize = transposer.t(getThumb().getSize());
+        if (getButtonUp() !is null)
+            area.height -= transposer.t(getButtonUp().getSize()).height;
+        if (getButtonDown() !is null)
+            area.height -= transposer.t(getButtonDown().getSize()).height;
+        Dimension sizeDifference = new Dimension(area.width,
+                                                    area.height - thumbSize.height);
+        dragRange = sizeDifference.height;
+        revertValue = getValue();
+        me.consume();
+    }
+
+    public void mouseDragged(MouseEvent me) {
+        if (!armed)
+            return;
+        Dimension difference = transposer.t(me.getLocation().getDifference(start));
+        int change = getValueRange() * difference.height / dragRange;
+        setValue(revertValue + change);
+        me.consume();
+    }
+
+    public void mouseReleased(MouseEvent me) {
+        if (!armed)
+            return;
+        armed = false;
+        me.consume();
+    }
+
+    public void mouseDoubleClicked(MouseEvent me) { }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ScrollBarLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,201 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ScrollBarLayout;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.geometry.Transposer;
+
+import dwtx.draw2d.AbstractLayout;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.ScrollBar;
+
+/**
+ * Lays out the Figures that make up a ScrollBar.
+ */
+public class ScrollBarLayout
+    : AbstractLayout
+{
+
+/** Used as a constraint for the up arrow figure. */
+public static final String UP_ARROW   = "up arrow";   //$NON-NLS-1$
+/** Used as a constraint for the down arrow figure. */
+public static final String DOWN_ARROW = "down arrow"; //$NON-NLS-1$
+/** Used as a constraint for the thumb figure. */
+public static final String THUMB      = "thumb";      //$NON-NLS-1$
+/** Used as a constraint for the page up figure. */
+public static final String PAGE_UP    = "page_up";    //$NON-NLS-1$
+/** Used as a constraint for the page down figure. */
+public static final String PAGE_DOWN  = "page_down";  //$NON-NLS-1$
+
+IFigure up, down, thumb, pageUp, pageDown;
+
+/**
+ * Transposes values if the ScrollBar is horizontally oriented. When used properly, the
+ * layout manager just needs to code for one case: vertical orientation.
+ */
+protected final Transposer transposer;
+
+/**
+ * Constructs a ScrollBarLayout. If the given Transposer is enabled, the Scrollbar will
+ * be horizontally oriented. Otherwise, the ScrollBar will be vertically oriented.
+ *
+ * @param t the Transposer
+ * @since 2.0
+ */
+public this(Transposer t) {
+    transposer = t;
+}
+
+/**
+ * @see AbstractLayout#setConstraint(IFigure, Object)
+ */
+public void setConstraint(IFigure figure, Object constraint_) {
+    String constraint = stringcast(constraint_);
+    if (constraint.equals(UP_ARROW))
+        up = figure;
+    else if (constraint.equals(DOWN_ARROW))
+        down = figure;
+    else if (constraint.equals(THUMB))
+        thumb = figure;
+    else if (constraint.equals(PAGE_UP))
+        pageUp = figure;
+    else if (constraint.equals(PAGE_DOWN))
+        pageDown = figure;
+}
+
+/**
+ * @see AbstractLayout#calculatePreferredSize(IFigure, int, int)
+ */
+protected Dimension calculatePreferredSize(IFigure parent, int w, int h) {
+    Insets insets = transposer.t(parent.getInsets());
+    Dimension d = new Dimension(16, 16 * 4);
+    d.expand(insets.getWidth(), insets.getHeight());
+    return transposer.t(d);
+}
+
+/**
+ * @see LayoutManager#layout(IFigure)
+ */
+public void layout(IFigure parent) {
+    ScrollBar scrollBar = cast(ScrollBar) parent;
+
+    Rectangle trackBounds = layoutButtons(scrollBar);
+
+    int extent = scrollBar.getExtent();
+    int max = scrollBar.getMaximum();
+    int min = scrollBar.getMinimum();
+    int totalRange =  max - min;
+    int valueRange = totalRange - extent;
+    if ((valueRange < 1) || (!scrollBar.isEnabled())) {
+        Rectangle boundsUpper = new Rectangle(trackBounds),
+                  boundsLower = new Rectangle(trackBounds);
+        boundsUpper.height /= 2;
+        boundsLower.y += boundsUpper.height;
+        boundsLower.height = trackBounds.height - boundsUpper.height;
+        if (pageUp !is null)
+            pageUp.setBounds(transposer.t(boundsUpper));
+        if (pageDown !is null)
+            pageDown.setBounds(transposer.t(boundsLower));
+        return;
+    }
+
+    if (totalRange is 0)
+        return;
+    int thumbHeight = Math.max(thumb is null ? 0 : thumb.getMinimumSize().height,
+                                trackBounds.height * extent / totalRange);
+
+    if (thumb !is null)
+        thumb.setVisible(trackBounds.height > thumbHeight);
+
+    int thumbY = trackBounds.y + (trackBounds.height - thumbHeight)
+                    * (scrollBar.getValue() - min) / valueRange;
+
+    Rectangle thumbBounds =  new Rectangle(
+        trackBounds.x,
+        thumbY,
+        trackBounds.width,
+        thumbHeight);
+
+    if (thumb !is null)
+        thumb.setBounds(transposer.t(thumbBounds));
+
+    if (pageUp !is null)
+        pageUp.setBounds(transposer.t(new Rectangle(
+            trackBounds.x,
+            trackBounds.y,
+            trackBounds.width,
+            thumbBounds.y - trackBounds.y)));
+
+    if (pageDown !is null)
+        pageDown.setBounds(transposer.t(new Rectangle(
+            trackBounds.x ,
+            thumbBounds.y + thumbHeight,
+            trackBounds.width,
+            trackBounds.bottom() - thumbBounds.bottom())));
+}
+
+/**
+ * Places the buttons and returns the Rectangle into which the track should be placed.
+ * The track consists of the pageup, pagedown, and thumb figures. The Rectangle returned
+ * should be transposed correctly, that is, it should be vertically oriented.  Users of
+ * the rectangle will re-transpose it for horizontal use.
+ *
+ * @param scrollBar the scrollbar whose buttons are being layed out
+ * @return the Rectangle into which the track should be placed
+ * @since 2.0
+ */
+protected Rectangle layoutButtons(ScrollBar scrollBar) {
+    Rectangle bounds = transposer.t(scrollBar.getClientArea());
+    Dimension buttonSize = new Dimension(
+        bounds.width,
+        Math.min(bounds.width, bounds.height / 2));
+
+    if (up !is null)
+        up.setBounds(transposer.t(
+            new Rectangle(bounds.getTopLeft(), buttonSize)));
+    if (down !is null) {
+        Rectangle r = new Rectangle (
+            bounds.x, bounds.bottom() - buttonSize.height,
+            buttonSize.width, buttonSize.height);
+        down.setBounds(transposer.t(r));
+    }
+
+    Rectangle trackBounds = bounds.getCropped(
+        new Insets(
+            (up   is null) ? 0 : buttonSize.height, 0,
+            (down is null) ? 0 : buttonSize.height, 0));
+
+    return trackBounds;
+}
+
+/**
+ * @see LayoutManager#remove(IFigure)
+ */
+public void remove(IFigure child) {
+    if (child is up) {
+        up = null;
+    } else if (child is down) {
+        down = null;
+    } else if (child is thumb) {
+        thumb = null;
+    } else if (child is pageUp) {
+        pageUp = null;
+    } else if (child is pageDown) {
+        pageDown = null;
+    }
+}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ScrollPane.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,337 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ScrollPane;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.Figure;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.ScrollBar;
+import dwtx.draw2d.Viewport;
+import dwtx.draw2d.ScrollPaneLayout;
+
+/**
+ * A class which implements automatic horizontal and/or vertical scrolling for a single
+ * IFigure child.
+ * <p>
+ * ScrollBar visibilites are represented by integer class constants:
+ * <ul>
+ *      <li>NEVER: Never show the ScrollBar
+ *      <li>AUTOMATIC: Show as needed, when the ScrollPane can no longer contain its view
+ *      <li>ALWAYS: Always show the ScrollBar
+ * </ul>
+ * To use, instantiate a ScrollPane object and call its setView(IFigure) method passing
+ * the IFigure that is desired to have scrolling ability.
+ */
+public class ScrollPane
+    : Figure
+{
+
+/** Constant indicating to never show the ScrollBar */
+public static const int NEVER = 0;
+/** Constant indicating to show as needed, when the ScrollPane can't contain its view */
+public static const int AUTOMATIC = 1;
+/** Constant indicating to always show the ScrollBar */
+public static const int ALWAYS = 2;
+
+/** The viewport being scrolled */
+protected Viewport viewport;
+/** The horizontal scrollbar */
+protected ScrollBar hBar;
+/** The vertical scrollbar */
+protected ScrollBar vBar;
+private int
+    hVisibility = AUTOMATIC,
+    vVisibility = AUTOMATIC;
+
+/**
+ * Constructs a new ScrollPane with a ScrollPaneLayout.
+ *
+ * @since 2.0
+ */
+public this() {
+    setLayoutManager(new ScrollPaneLayout());
+}
+
+/**
+ * Creates a new horizontally oriented ScrollBar and adds it to this ScrollPane.
+ *
+ * @since 2.0
+ */
+protected void createHorizontalScrollBar() {
+    ScrollBar bar = new ScrollBar();
+    bar.setHorizontal(true);
+    setHorizontalScrollBar(bar);
+}
+
+/**
+ * Creates a new Viewport and adds it to this ScrollPane.
+ *
+ * @since 2.0
+ */
+protected void createViewport() {
+    setViewport(new Viewport());
+}
+
+/**
+ * Creates a new vertically oriented ScrollBar and adds it to this ScrollPane.
+ *
+ * @since 2.0
+ */
+protected void createVerticalScrollBar() {
+    ScrollBar bar = new ScrollBar();
+    setVerticalScrollBar(bar);
+}
+
+/**
+ * Returns the ScrollPane's horizontal ScrollBar.
+ *
+ * @return the horizontal scrollbar
+ * @since 2.0
+ */
+public ScrollBar getHorizontalScrollBar() {
+    if (hBar is null)
+        createHorizontalScrollBar();
+    return hBar;
+}
+
+/**
+ * Returns the visibility of the ScrollPane's horizontal ScrollBar. These are represented
+ * by the integer class constants {@link #NEVER}, {@link #AUTOMATIC}, and {@link #ALWAYS}.
+ * The default is {@link #AUTOMATIC}.
+ *
+ * @return the visiblity of the horizontal scrollbar
+ * @since 2.0
+ */
+public int getHorizontalScrollBarVisibility() {
+    return hVisibility;
+}
+
+/**
+ * Returns the ScrollPane's vertical ScrollBar.
+ *
+ * @return teh vertical scrollbar
+ * @since 2.0
+ */
+public ScrollBar getVerticalScrollBar() {
+    if (vBar is null)
+        createVerticalScrollBar();
+    return vBar;
+}
+
+/**
+ * Returns the visibility of the ScrollPane's vertical ScrollBar. These are represented
+ * by the integer class constants {@link #NEVER}, {@link #AUTOMATIC}, and {@link #ALWAYS}.
+ * The default is {@link #AUTOMATIC}.
+ *
+ * @return the visibility of the vertical scrollbar
+ * @since 2.0
+ */
+public int getVerticalScrollBarVisibility() {
+    return vVisibility;
+}
+
+/**
+ * Returns the contents of the viewport.
+ * @return the contents of the viewport
+ */
+public IFigure getContents() {
+    return getView();
+}
+
+/**
+ * Returns the ScrollPane's view. The view is the IFigure that is the contents of the
+ * ScrollPane.
+ * @return the contents
+ * @deprecated use getContents()
+ * @since 2.0
+ */
+public IFigure getView() {
+    return getViewport().getContents();
+}
+
+/**
+ * Returns the ScrollPane's {@link Viewport}.
+ *
+ * @return the viewport
+ * @since 2.0
+ */
+public Viewport getViewport() {
+    if (viewport is null)
+        createViewport();
+    return viewport;
+}
+
+/**
+ * Returns true because ScrollPanes are always opaque.
+ * @see IFigure#isOpaque()
+ */
+public bool isOpaque() {
+    return true;
+}
+
+/**
+ * Scrolls the Scrollpane horizontally x pixels from its left-most position.
+ *
+ * @param x the amount to scroll horizontally
+ * @since 2.0
+ */
+public void scrollHorizontalTo(int x) {
+    getViewport().setHorizontalLocation(x);
+}
+
+/**
+ * Scrolls the Scrollpane horizontally from its left-most position by location.x pixels
+ * and vertically from its top-most position by location.y pixels.
+ *
+ * @param location the point to scroll to
+ * @since 2.0
+ */
+public void scrollTo(Point location) {
+    scrollHorizontalTo(location.x);
+    scrollVerticalTo(location.y);
+}
+
+/**
+ * Scrolls the Scrollpane vertically y pixels from its top-most position.
+ *
+ * @param y the amount to scroll vertically
+ * @since 2.0
+ */
+public void scrollVerticalTo(int y) {
+    getViewport().setVerticalLocation(y);
+}
+
+/**
+ * Sets the contents of the current viewport.
+ * @param figure the contents of the viewport
+ */
+public void setContents(IFigure figure) {
+    setView(figure);
+}
+
+/**
+ * Sets the ScrollPane's horizontal ScrollBar to the passed ScrollBar.
+ *
+ * @param bar the new horizontal scrollbar
+ * @since 2.0
+ */
+public void setHorizontalScrollBar(ScrollBar bar) {
+    if (hBar !is null) {
+        remove(hBar);
+        hBar.getRangeModel().removePropertyChangeListener(hBar);
+    }
+    hBar = bar;
+    if (hBar !is null) {
+        add(hBar);
+        hBar.setRangeModel(getViewport().getHorizontalRangeModel());
+    }
+}
+
+/**
+ * Sets the horizontal ScrollBar visibility of the ScrollPane to the passed value. These
+ * are represented by the integer class constants {@link #NEVER}, {@link #AUTOMATIC}, and
+ * {@link #ALWAYS}. The default is {@link #AUTOMATIC}.
+ *
+ * @param v the new horizontal visibility
+ * @since 2.0
+ */
+public void setHorizontalScrollBarVisibility(int v) {
+    if (hVisibility is v)
+        return;
+    hVisibility = v;
+    revalidate();
+}
+
+/**
+ * Sets both the horizontal and vertical ScrollBar visibilities of the ScrollPane to the
+ * passed value. These are represented by the integer class constants {@link #NEVER},
+ * {@link #AUTOMATIC}, and {@link #ALWAYS}. The default is {@link #AUTOMATIC}.
+ *
+ * @param v the new vertical and horizontal visibility
+ * @since 2.0
+ */
+public void setScrollBarVisibility(int v) {
+    setHorizontalScrollBarVisibility(v);
+    setVerticalScrollBarVisibility(v);
+}
+
+/**
+ * Sets the ScrollPane's vertical ScrollBar to the passed Scrollbar.
+ *
+ * @param bar the new vertical scrollbar
+ * @since 2.0
+ */
+public void setVerticalScrollBar(ScrollBar bar) {
+    if (vBar !is null) {
+        remove(vBar);
+        vBar.getRangeModel().removePropertyChangeListener(vBar);
+    }
+    vBar = bar;
+    if (vBar !is null) {
+        add(vBar);
+        vBar.setRangeModel(getViewport().getVerticalRangeModel());
+    }
+}
+
+/**
+ * Sets the vertical ScrollBar visibility of the ScrollPane to the passed value. These are
+ * represented by the integer class constants {@link #NEVER}, {@link #AUTOMATIC}, and
+ * {@link #ALWAYS}. The default is {@link #AUTOMATIC}.
+ *
+ * @param v the new vertical scrollbar visibility
+ * @since 2.0
+ */
+public void setVerticalScrollBarVisibility(int v) {
+    if (vVisibility is v)
+        return;
+    vVisibility = v;
+    revalidate();
+}
+
+/**
+ * Sets the ScrollPane's view to the passed IFigure. The view is the top-level IFigure
+ * which represents the contents of the ScrollPane.
+ * @param figure the new contents
+ * @deprecated call setContents(IFigure) instead
+ * @since 2.0
+ */
+public void setView(IFigure figure) {
+    getViewport().setContents(figure);
+}
+
+/**
+ * Sets the ScrollPane's Viewport to the passed value.
+ *
+ * @param vp the new viewport
+ * @since 2.0
+ */
+public void setViewport(Viewport vp) {
+    if (viewport !is null)
+        remove(viewport);
+    viewport = vp;
+    if (vp !is null)
+        add(vp, 0);
+}
+
+/**
+ * @see IFigure#validate()
+ */
+public void validate() {
+    super.validate();
+    getHorizontalScrollBar().validate();
+    getVerticalScrollBar().validate();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ScrollPaneLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ScrollPaneLayout;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Rectangle;
+
+import dwtx.draw2d.AbstractHintLayout;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.ScrollPane;
+import dwtx.draw2d.ScrollBar;
+import dwtx.draw2d.Viewport;
+import dwtx.draw2d.ScrollPaneSolver;
+
+/**
+ * The ScrollPaneLayout is responsible for laying out the {@link Viewport} and
+ * {@link ScrollBar ScrollBars} of a {@link ScrollPane}.
+ */
+public class ScrollPaneLayout
+    : AbstractHintLayout
+{
+
+/** @see ScrollPane#NEVER */
+protected static const int NEVER  = ScrollPane.NEVER;
+/** @see ScrollPane#AUTOMATIC */
+protected static const int AUTO   = ScrollPane.AUTOMATIC;
+/** @see ScrollPane#ALWAYS */
+protected static const int ALWAYS = ScrollPane.ALWAYS;
+
+/**
+ * @see AbstractHintLayout#calculateMinimumSize(IFigure, int, int)
+ */
+public Dimension calculateMinimumSize(IFigure figure, int w, int h) {
+    ScrollPane scrollpane = cast(ScrollPane)figure;
+    Insets insets = scrollpane.getInsets();
+    Dimension d = scrollpane.getViewport().getMinimumSize(w, h);
+    return d.getExpanded(insets.getWidth(), insets.getHeight());
+}
+
+/**
+ * Calculates and returns the preferred size of the container based on the given hints.
+ * If the given ScrollPane's (<i>container</i>) horizontal and vertical scroll bar
+ * visibility is not {@link ScrollPane#NEVER}, then space for those bars is always
+ * deducted from the hints (whether or not we actually need the scroll bars).
+ *
+ * @param container the ScrollPane whose preferred size needs to be calculated
+ * @param wHint the width hint
+ * @param hHint the height hint
+ * @return the preferred size of the given container
+ * @since   2.0
+ */
+protected Dimension calculatePreferredSize(IFigure container, int wHint, int hHint) {
+    ScrollPane scrollpane = cast(ScrollPane)container;
+    ScrollBar hBar = scrollpane.getHorizontalScrollBar();
+    ScrollBar vBar = scrollpane.getVerticalScrollBar();
+    Insets insets = scrollpane.getInsets();
+
+    int reservedWidth = insets.getWidth();
+    int reservedHeight = insets.getHeight();
+
+    if (scrollpane.getVerticalScrollBarVisibility() !is ScrollPane.NEVER)
+        reservedWidth += vBar.getPreferredSize().width;
+    if (scrollpane.getHorizontalScrollBarVisibility() !is ScrollPane.NEVER)
+        reservedHeight += hBar.getPreferredSize().height;
+
+    if (wHint > -1)
+        wHint = Math.max(0, wHint - reservedWidth);
+    if (hHint > -1)
+        hHint = Math.max(0, hHint - reservedHeight);
+
+    return scrollpane
+        .getViewport()
+        .getPreferredSize(wHint, hHint)
+        .getExpanded(reservedWidth, reservedHeight);
+}
+
+/**
+ * @see dwtx.draw2d.LayoutManager#layout(IFigure)
+ */
+public void layout(IFigure parent) {
+    ScrollPane scrollpane = cast(ScrollPane)parent;
+    Viewport viewport = scrollpane.getViewport();
+    ScrollBar hBar = scrollpane.getHorizontalScrollBar(),
+              vBar = scrollpane.getVerticalScrollBar();
+
+    ScrollPaneSolver.Result result = ScrollPaneSolver.solve(
+                    parent.getClientArea(),
+                    viewport,
+                    scrollpane.getHorizontalScrollBarVisibility(),
+                    scrollpane.getVerticalScrollBarVisibility(),
+                    vBar.getPreferredSize().width,
+                    hBar.getPreferredSize().height);
+
+    if (result.showV) {
+        vBar.setBounds(new Rectangle(
+            result.viewportArea.right(),
+            result.viewportArea.y,
+            result.insets.right,
+            result.viewportArea.height));
+    }
+    if (result.showH) {
+        hBar.setBounds(new Rectangle(
+            result.viewportArea.x,
+            result.viewportArea.bottom(),
+            result.viewportArea.width,
+            result.insets.bottom));
+    }
+    vBar.setVisible(result.showV);
+    hBar.setVisible(result.showH);
+
+    int vStepInc = vBar.getStepIncrement();
+    int vPageInc = vBar.getRangeModel().getExtent() - vStepInc;
+    if (vPageInc < vStepInc)
+        vPageInc = vStepInc;
+    vBar.setPageIncrement(vPageInc);
+
+    int hStepInc = hBar.getStepIncrement();
+    int hPageInc = hBar.getRangeModel().getExtent() - hStepInc;
+    if (hPageInc < hStepInc)
+        hPageInc = hStepInc;
+    hBar.setPageIncrement(hPageInc);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ScrollPaneSolver.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,124 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ScrollPaneSolver;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.Viewport;
+
+/**
+ * This class handles the calculation of solving for the area of a
+ * {@link dwtx.draw2d.ScrollPane}'s viewport and insets. Also determines if
+ * the horizontal and vertical scrollbars should be visible.
+ */
+public class ScrollPaneSolver {
+
+/** Scrollbar visibility constants -- never show scrollbars **/
+public static const int NEVER = 0;
+/** Scrollbar visibility constants -- show scrollbars automatically **/
+public static const int AUTOMATIC = 1;
+/** Scrollbar visibility constants -- always show scrollbars **/
+public static const int ALWAYS = 2;
+
+/**
+ * Container class for the results of ScrollPaneSolver's solve method
+ */
+public static class Result {
+    /** Show horizontal scrollbar bool **/
+    public bool showH;
+
+    /** Show vertical scrollbar bool **/
+    public bool showV;
+
+    /** Area of ScrollPane's viewport **/
+    public Rectangle viewportArea;
+
+    /** Insets of ScrollPane **/
+    public Insets insets;
+}
+
+/**
+ * Solves for the viewport area, insets, and visibility of horizontal and vertical
+ * scrollbars of a ScrollPane
+ * @param clientArea The ScrollPane's client area
+ * @param viewport The ScrollPane's Viewport
+ * @param hVis Horizontal scrollbar visibility
+ * @param vVis Vertical scrollbar visibility
+ * @param vBarWidth Width of vertical scrollbar
+ * @param hBarHeight Height of horizontal scrollbar
+ * @return the Result
+ */
+public static Result solve(Rectangle clientArea, Viewport viewport, int hVis, int vVis,
+                            int vBarWidth, int hBarHeight) {
+    Result result = new Result();
+    result.insets = new Insets();
+    result.insets.bottom = hBarHeight;
+    result.insets.right  = vBarWidth;
+
+    Dimension available  = clientArea.getSize();
+    Dimension guaranteed = (new Dimension(available)).shrink(
+                (vVis is NEVER ? 0 : result.insets.right),
+                (hVis is NEVER ? 0 : result.insets.bottom));
+    guaranteed.width = Math.max(guaranteed.width, 0);
+    guaranteed.height = Math.max(guaranteed.height, 0);
+    int wHint = guaranteed.width;
+    int hHint = guaranteed.height;
+
+    Dimension preferred  = viewport.getPreferredSize(wHint, hHint).getCopy();
+    Insets viewportInsets = viewport.getInsets();
+    /*
+     * This was calling viewport.getMinimumSize(), but viewport's minimum size was really
+     * small, and wasn't a function of its contents.
+     */
+    Dimension viewportMinSize = new Dimension(
+            viewportInsets.getWidth(), viewportInsets.getHeight());
+    if (viewport.getContents() !is null) {
+        if (viewport.getContentsTracksHeight() && hHint > -1)
+            hHint = Math.max(0, hHint - viewportInsets.getHeight());
+        if (viewport.getContentsTracksWidth() && wHint > -1)
+            wHint = Math.max(0, wHint - viewportInsets.getWidth());
+        viewportMinSize.expand(
+            viewport.getContents().getMinimumSize(wHint, hHint));
+    }
+
+    /*
+     * Adjust preferred size if tracking flags set.  Basically, tracking is "compress view
+     * until its minimum size is reached".
+     */
+    if (viewport.getContentsTracksHeight())
+        preferred.height = viewportMinSize.height;
+    if (viewport.getContentsTracksWidth())
+        preferred.width = viewportMinSize.width;
+
+    bool none = available.contains(preferred),
+            both = !none && preferred.containsProper(guaranteed),
+           showV = both || preferred.height > available.height,
+           showH = both || preferred.width  > available.width;
+
+    //Adjust for visibility override flags
+    result.showV = vVis !is NEVER && (showV || vVis is ALWAYS);
+    result.showH = hVis !is NEVER && (showH || hVis is ALWAYS);
+
+    if (!result.showV)
+        result.insets.right = 0;
+    if (!result.showH)
+        result.insets.bottom = 0;
+    result.viewportArea = clientArea.getCropped(result.insets);
+    viewport.setBounds(result.viewportArea);
+    return result;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Shape.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.Shape;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.Figure;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.ColorConstants;
+
+/**
+ * Provides abstract support for a variety of shapes.
+ * <p>
+ * When customizing shapes, you shouldn't override paintFigure().  Override fillShape()
+ * and outlineShape() methods instead.
+ */
+public abstract class Shape
+    : Figure
+{
+
+/** The width of this shape's outline. */
+protected int lineWidth = 1;
+/** The line style to be used for this shape's outline. */
+protected int lineStyle;
+
+private bool
+    fill = true,
+    outline = true,
+    xorFill,
+    xorOutline;
+
+/**
+ * Default constructor.
+ *
+ * @since 2.0
+ */
+public this() {
+    lineStyle = Graphics.LINE_SOLID;
+}
+
+/**
+ * Fills the interior of the shape with the background color.
+ * @param graphics the graphics object
+ */
+protected abstract void fillShape(Graphics graphics);
+
+/**
+ * Returns the line style used to outline this shape.
+ * @return the line style
+ */
+public int getLineStyle() {
+    return lineStyle;
+}
+
+/**
+ * Returns the line width of this shape's outline.
+ * @return the line width
+ */
+public int getLineWidth() {
+    return lineWidth;
+}
+
+/**
+ * Returns <code>false</code> as shapes only draw themselves onto other figures, and
+ * generally dont have rectangular dimensions.
+ *
+ * @see Figure#isOpaque()
+ * @since 2.0
+ */
+public bool isOpaque() {
+    return false;
+}
+
+/**
+ * Outlines this shape using the foreground color.
+ * @param graphics the graphics object
+ */
+protected abstract void outlineShape(Graphics graphics);
+
+/**
+ * Paints the shape. Each shape has an outline to draw, and a region to fill within that
+ * outline. Disabled shapes must visually depict the disabled state.
+ *
+ * @see Figure#paintFigure(Graphics)
+ */
+public void paintFigure(Graphics graphics) {
+    if (!isEnabled()) {
+        graphics.translate(1, 1);
+        graphics.setBackgroundColor(ColorConstants.buttonLightest);
+        graphics.setForegroundColor(ColorConstants.buttonLightest);
+        if (fill) {
+            graphics.setXORMode(xorFill);
+            fillShape(graphics);
+        }
+        if (outline) {
+            graphics.setXORMode(xorOutline);
+            graphics.setLineStyle(lineStyle);
+            graphics.setLineWidth(lineWidth);
+            outlineShape(graphics);
+        }
+        graphics.setBackgroundColor(ColorConstants.buttonDarker);
+        graphics.setForegroundColor(ColorConstants.buttonDarker);
+        graphics.translate(-1, -1);
+    }
+    if (fill) {
+        graphics.setXORMode(xorFill);
+        fillShape(graphics);
+    }
+    if (outline) {
+        graphics.setXORMode(xorOutline);
+        graphics.setLineStyle(lineStyle);
+        graphics.setLineWidth(lineWidth);
+        outlineShape(graphics);
+    }
+}
+
+/**
+ * Sets whether this shape should fill its region or not. It repaints this figure.
+ *
+ * @param b fill state
+ * @since 2.0
+ */
+public void setFill(bool b) {
+    if (fill is b)
+        return;
+    fill = b;
+    repaint();
+}
+
+/**
+ * Sets whether XOR based fill should be used by the shape. It repaints this figure.
+ *
+ * @param b XOR fill state
+ * @since 2.0
+ */
+public void setFillXOR(bool b) {
+    if (xorFill is b)
+        return;
+    xorFill = b;
+    repaint();
+}
+
+/**
+ * Sets the line width to be used to outline the shape.
+ *
+ * @param w the new width
+ * @since 2.0
+ */
+public void setLineWidth(int w) {
+    if (lineWidth is w)
+        return;
+    lineWidth = w;
+    repaint();
+}
+
+/**
+ * Sets the style of line to be used by this shape.
+ *
+ * @param s the new line style
+ * @since 2.0
+ */
+public void setLineStyle(int s) {
+    if (lineStyle is s)
+        return;
+    lineStyle = s;
+    repaint();
+}
+
+/**
+ * Sets whether the outline should be drawn for this shape.
+ *
+ * @param b <code>true</code> if the shape should be outlined
+ * @since 2.0
+ */
+public void setOutline(bool b) {
+    if (outline is b)
+        return;
+    outline = b;
+    repaint();
+}
+
+/**
+ * Sets whether XOR based outline should be used for this shape.
+ *
+ * @param b <code>true</code> if the outline should be XOR'ed
+ * @since 2.0
+ */
+public void setOutlineXOR(bool b) {
+    if (xorOutline is b)
+        return;
+    xorOutline = b;
+    repaint();
+}
+
+/**
+ * Sets whether XOR based fill and XOR based outline should be used for this shape.
+ *
+ * @param b <code>true</code> if the outline and fill should be XOR'ed
+ * @since 2.0
+ */
+public void setXOR(bool b) {
+    xorOutline = xorFill = b;
+    repaint();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ShortestPathConnectionRouter.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,317 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ShortestPathConnectionRouter;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.geometry.PrecisionPoint;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.graph.Path;
+import dwtx.draw2d.graph.ShortestPathRouter;
+import dwtx.draw2d.AbstractRouter;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.LayoutListener;
+import dwtx.draw2d.FigureListener;
+import dwtx.draw2d.Connection;
+import dwtx.draw2d.Bendpoint;
+
+/**
+ * Routes multiple connections around the children of a given container figure.
+ * @author Whitney Sorenson
+ * @author Randy Hudson
+ * @since 3.1
+ */
+public final class ShortestPathConnectionRouter
+    : AbstractRouter
+{
+
+private class LayoutTracker : LayoutListenerStub {
+    public void postLayout(IFigure container) {
+        processLayout();
+    }
+    public void remove(IFigure child) {
+        removeChild(child);
+    }
+    public void setConstraint(IFigure child, Object constraint) {
+        addChild(child);
+    }
+}
+
+private Map constraintMap;
+private Map figuresToBounds;
+private Map connectionToPaths;
+private bool isDirty;
+private ShortestPathRouter algorithm;
+private IFigure container;
+private Set staleConnections;
+private LayoutListener listener;
+
+private FigureListener figureListener;
+private void initFigureListener(){
+    figureListener = new class() FigureListener {
+        public void figureMoved(IFigure source) {
+            Rectangle newBounds = source.getBounds().getCopy();
+            if (algorithm.updateObstacle(cast(Rectangle)figuresToBounds.get(cast(Object)source), newBounds)) {
+                queueSomeRouting();
+                isDirty = true;
+            }
+
+            figuresToBounds.put(cast(Object)source, newBounds);
+        }
+    };
+}
+private bool ignoreInvalidate;
+
+/**
+ * Creates a new shortest path router with the given container. The container
+ * contains all the figure's which will be treated as obstacles for the connections to
+ * avoid. Any time a child of the container moves, one or more connections will be
+ * revalidated to process the new obstacle locations. The connections being routed must
+ * not be contained within the container.
+ *
+ * @param container the container
+ */
+public this(IFigure container) {
+    initFigureListener();
+    constraintMap = new HashMap();
+    algorithm = new ShortestPathRouter();
+    staleConnections = new HashSet();
+    listener = new LayoutTracker();
+    isDirty = false;
+    algorithm = new ShortestPathRouter();
+    this.container = container;
+}
+
+void addChild(IFigure child) {
+    if (connectionToPaths is null)
+        return;
+    if (figuresToBounds.containsKey(cast(Object)child))
+        return;
+    Rectangle bounds = child.getBounds().getCopy();
+    algorithm.addObstacle(bounds);
+    figuresToBounds.put(cast(Object)child, bounds);
+    child.addFigureListener(figureListener);
+    isDirty = true;
+}
+
+private void hookAll() {
+    figuresToBounds = new HashMap();
+    for (int i = 0; i < container.getChildren().size(); i++)
+        addChild(cast(IFigure)container.getChildren().get(i));
+    container.addLayoutListener(listener);
+}
+
+private void unhookAll() {
+    container.removeLayoutListener(listener);
+    if (figuresToBounds !is null) {
+        Iterator figureItr = figuresToBounds.keySet().iterator();
+        while (figureItr.hasNext()) {
+            //Must use iterator's remove to avoid concurrent modification
+            IFigure child = cast(IFigure)figureItr.next();
+            figureItr.remove();
+            removeChild(child);
+        }
+        figuresToBounds = null;
+    }
+}
+
+/**
+ * Gets the constraint for the given {@link Connection}.  The constraint is the paths
+ * list of bend points for this connection.
+ *
+ * @param connection The connection whose constraint we are retrieving
+ * @return The constraint
+ */
+public Object getConstraint(Connection connection) {
+    return constraintMap.get(cast(Object)connection);
+}
+
+/**
+ * Returns the default spacing maintained on either side of a connection. The default
+ * value is 4.
+ * @return the connection spacing
+ * @since 3.2
+ */
+public int getSpacing() {
+    return algorithm.getSpacing();
+}
+
+/**
+ * @see ConnectionRouter#invalidate(Connection)
+ */
+public void invalidate(Connection connection) {
+    if (ignoreInvalidate)
+        return;
+    staleConnections.add(cast(Object)connection);
+    isDirty = true;
+}
+
+private void processLayout() {
+    if (staleConnections.isEmpty())
+        return;
+    (cast(Connection)staleConnections.iterator().next()).revalidate();
+}
+
+private void processStaleConnections() {
+    Iterator iter = staleConnections.iterator();
+    if (iter.hasNext() && connectionToPaths is null) {
+        connectionToPaths = new HashMap();
+        hookAll();
+    }
+
+    while (iter.hasNext()) {
+        Connection conn = cast(Connection)iter.next();
+
+        Path path = cast(Path)connectionToPaths.get(cast(Object)conn);
+        if (path is null) {
+            path = new Path(cast(Object)conn);
+            connectionToPaths.put(cast(Object)conn, path);
+            algorithm.addPath(path);
+        }
+
+        List constraint = cast(List)getConstraint(conn);
+        if (constraint is null)
+            constraint = Collections.EMPTY_LIST;
+
+        Point start = conn.getSourceAnchor().getReferencePoint().getCopy();
+        Point end = conn.getTargetAnchor().getReferencePoint().getCopy();
+
+        container.translateToRelative(start);
+        container.translateToRelative(end);
+
+        path.setStartPoint(start);
+        path.setEndPoint(end);
+
+        if (!constraint.isEmpty()) {
+            PointList bends = new PointList(constraint.size());
+            for (int i = 0; i < constraint.size(); i++) {
+                Bendpoint bp = cast(Bendpoint)constraint.get(i);
+                bends.addPoint(bp.getLocation());
+            }
+            path.setBendPoints(bends);
+        } else
+            path.setBendPoints(null);
+
+        isDirty |= path.isDirty;
+    }
+    staleConnections.clear();
+}
+
+void queueSomeRouting() {
+    if (connectionToPaths is null || connectionToPaths.isEmpty())
+        return;
+    try {
+        ignoreInvalidate = true;
+        (cast(Connection)connectionToPaths.keySet().iterator().next())
+            .revalidate();
+    } finally {
+        ignoreInvalidate = false;
+    }
+}
+
+/**
+ * @see ConnectionRouter#remove(Connection)
+ */
+public void remove(Connection connection) {
+    staleConnections.remove(cast(Object)connection);
+    constraintMap.remove(cast(Object)connection);
+    if (connectionToPaths is null)
+        return;
+    Path path = cast(Path)connectionToPaths.remove(cast(Object)connection);
+    algorithm.removePath(path);
+    isDirty = true;
+    if (connectionToPaths.isEmpty()) {
+        unhookAll();
+        connectionToPaths = null;
+    } else {
+        //Make sure one of the remaining is revalidated so that we can re-route again.
+        queueSomeRouting();
+    }
+}
+
+void removeChild(IFigure child) {
+    if (connectionToPaths is null)
+        return;
+    Rectangle bounds = child.getBounds().getCopy();
+    bool change = algorithm.removeObstacle(bounds);
+    figuresToBounds.remove(cast(Object)child);
+    child.removeFigureListener(figureListener);
+    if (change) {
+        isDirty = true;
+        queueSomeRouting();
+    }
+}
+
+/**
+ * @see ConnectionRouter#route(Connection)
+ */
+public void route(Connection conn) {
+    if (isDirty) {
+        ignoreInvalidate = true;
+        processStaleConnections();
+        isDirty = false;
+        List updated = algorithm.solve();
+        Connection current;
+        for (int i = 0; i < updated.size(); i++) {
+            Path path = cast(Path) updated.get(i);
+            current = cast(Connection)path.data;
+            current.revalidate();
+
+            PointList points = path.getPoints().getCopy();
+            Point ref1, ref2, start, end;
+            ref1 = new PrecisionPoint(points.getPoint(1));
+            ref2 = new PrecisionPoint(points.getPoint(points.size() - 2));
+            current.translateToAbsolute(ref1);
+            current.translateToAbsolute(ref2);
+
+            start = current.getSourceAnchor().getLocation(ref1).getCopy();
+            end = current.getTargetAnchor().getLocation(ref2).getCopy();
+
+            current.translateToRelative(start);
+            current.translateToRelative(end);
+            points.setPoint(start, 0);
+            points.setPoint(end, points.size() - 1);
+
+            current.setPoints(points);
+        }
+        ignoreInvalidate = false;
+    }
+}
+
+/**
+ * @see ConnectionRouter#setConstraint(Connection, Object)
+ */
+public void setConstraint(Connection connection, Object constraint) {
+    //Connection.setConstraint() already calls revalidate, so we know that a
+    // route() call will follow.
+    staleConnections.add(cast(Object)connection);
+    constraintMap.put(cast(Object)connection, constraint);
+    isDirty = true;
+}
+
+/**
+ * Sets the default space that should be maintained on either side of a connection. This
+ * causes the connections to be separated from each other and from the obstacles. The
+ * default value is 4.
+ *
+ * @param spacing the connection spacing
+ * @since 3.2
+ */
+public void setSpacing(int spacing) {
+    algorithm.setSpacing(spacing);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/SimpleEtchedBorder.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.SimpleEtchedBorder;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.SchemeBorder;
+import dwtx.draw2d.Border;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.FigureUtilities;
+
+/**
+ * Provides a two pixel wide constant sized border, having an etched look.
+ */
+public final class SimpleEtchedBorder
+    : SchemeBorder
+{
+
+/** The singleton instance of this class */
+public static const Border singleton;
+
+/** The insets */
+protected static const Insets INSETS;
+
+static this(){
+    singleton = new SimpleEtchedBorder();
+    INSETS = new Insets(2);
+}
+/**
+ * Constructs a default border having a two pixel wide border.
+ *
+ * @since 2.0
+ */
+protected this() { }
+
+/**
+ * Returns the Insets used by this border. This is a constant value of two pixels in each
+ * direction.
+ * @see Border#getInsets(IFigure)
+ */
+public Insets getInsets(IFigure figure) {
+    return new Insets(INSETS);
+}
+
+/**
+ * Returns the opaque state of this border. This border is opaque and takes responsibility
+ * to fill the region it encloses.
+ * @see Border#isOpaque()
+ */
+public bool isOpaque() {
+    return true;
+}
+
+/**
+ * @see Border#paint(IFigure, Graphics, Insets)
+ */
+public void paint(IFigure figure, Graphics g, Insets insets) {
+    Rectangle rect = getPaintRectangle(figure, insets);
+    FigureUtilities.paintEtchedBorder(g, rect);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/SimpleLoweredBorder.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.SimpleLoweredBorder;
+
+import dwt.dwthelper.utils;
+
+import dwt.graphics.Color;
+import dwtx.draw2d.SchemeBorder;
+import dwtx.draw2d.ColorConstants;
+
+/**
+ * Provides a lowered border.
+ */
+public final class SimpleLoweredBorder
+    : SchemeBorder
+{
+
+private static const Scheme DOUBLE;
+
+static this(){
+    DOUBLE = new Scheme(
+        [ColorConstants.buttonDarkest,  ColorConstants.buttonDarker],
+        [ColorConstants.buttonLightest, ColorConstants.button] );
+}
+/**
+ * Constructs a SimpleLoweredBorder with the predefined button-pressed Scheme set as
+ * default.
+ *
+ * @since 2.0
+ */
+public this() {
+    super(SCHEMES.BUTTON_PRESSED);
+}
+
+/**
+ * Constructs a SimpleLoweredBorder with the width of all sides provided as input. If
+ * width is 2, this SimpleLoweredBorder will use the local DOUBLE Scheme, otherwise it
+ * will use the {@link SchemeBorder.SCHEMES#BUTTON_PRESSED} Scheme.
+ *
+ * @param width the width of all the sides of the border
+ * @since 2.0
+ */
+public this(int width) {
+    super(width is 2 ? DOUBLE : SCHEMES.BUTTON_PRESSED);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/SimpleRaisedBorder.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.SimpleRaisedBorder;
+
+import dwt.dwthelper.utils;
+
+import dwt.graphics.Color;
+import dwtx.draw2d.SchemeBorder;
+import dwtx.draw2d.ColorConstants;
+
+/**
+ * Provides a raised border.
+ */
+public class SimpleRaisedBorder
+    : SchemeBorder
+{
+
+private static const Scheme DOUBLE;
+static this(){
+    DOUBLE = new Scheme(
+        [ColorConstants.buttonLightest, ColorConstants.button],
+        [ColorConstants.buttonDarkest,  ColorConstants.buttonDarker]
+    );
+}
+
+/**
+ * Constructs a SimpleRaisedBorder with the predefined {@link SchemeBorder.SCHEMES#BUTTON_RAISED}
+ * Scheme set as default.
+ *
+ * @since 2.0
+ */
+public this() {
+    super(SCHEMES.BUTTON_RAISED);
+}
+
+/**
+ * Constructs a SimpleRaisedBorder with the width of all sides provided as input. If
+ * width is 2, this SimpleRaisedBorder will use the local DOUBLE Scheme, otherwise it will
+ * use the {@link SchemeBorder.SCHEMES#BUTTON_RAISED} Scheme.
+ *
+ * @param width the width of all the sides of the border
+ * @since 2.0
+ */
+public this(int width) {
+    super(width is 2 ? DOUBLE : SCHEMES.BUTTON_RAISED);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/StackLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.StackLayout;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.AbstractHintLayout;
+import dwtx.draw2d.IFigure;
+
+/**
+ * Figures using the StackLayout as their layout manager have their children placed on top
+ * of one another. Order of placement is determined by the order in which the children
+ * were added, first child added placed on the bottom.
+ */
+public class StackLayout
+    : AbstractHintLayout
+{
+
+/**
+ * Returns the minimum size required by the input container. This is the size of the
+ * largest child of the container, as all other children fit into this size.
+ *
+ * @see AbstractHintLayout#calculateMinimumSize(IFigure, int, int)
+ */
+protected Dimension calculateMinimumSize(IFigure figure, int wHint, int hHint) {
+    if (wHint > -1)
+        wHint = Math.max(0, wHint - figure.getInsets().getWidth());
+    if (hHint > -1)
+        hHint = Math.max(0, hHint - figure.getInsets().getHeight());
+    Dimension d = new Dimension();
+    List children = figure.getChildren();
+    IFigure child;
+    for (int i = 0; i < children.size(); i++) {
+        child = cast(IFigure)children.get(i);
+        if (!isObservingVisibility() || child.isVisible())
+            d.union_(child.getMinimumSize(wHint, hHint));
+    }
+
+    d.expand(figure.getInsets().getWidth(),
+             figure.getInsets().getHeight());
+    d.union_(getBorderPreferredSize(figure));
+    return d;
+
+}
+
+/**
+ * Calculates and returns the preferred size of the given figure.  This is the union of
+ * the preferred sizes of the widest and the tallest of all its children.
+ *
+ * @see AbstractLayout#calculatePreferredSize(IFigure, int, int)
+ */
+protected Dimension calculatePreferredSize(IFigure figure, int wHint, int hHint) {
+    if (wHint > -1)
+        wHint = Math.max(0, wHint - figure.getInsets().getWidth());
+    if (hHint > -1)
+        hHint = Math.max(0, hHint - figure.getInsets().getHeight());
+    Dimension d = new Dimension();
+    List children = figure.getChildren();
+    IFigure child;
+    for (int i = 0; i < children.size(); i++) {
+        child = cast(IFigure)children.get(i);
+        if (!isObservingVisibility() || child.isVisible())
+            d.union_(child.getPreferredSize(wHint, hHint));
+    }
+
+    d.expand(figure.getInsets().getWidth(),
+             figure.getInsets().getHeight());
+    d.union_(getBorderPreferredSize(figure));
+    return d;
+}
+
+/**
+ * @see dwtx.draw2d.LayoutManager#layout(IFigure)
+ */
+public void layout(IFigure figure) {
+    Rectangle r = figure.getClientArea();
+    List children = figure.getChildren();
+    IFigure child;
+    for (int i = 0; i < children.size(); i++) {
+        child = cast(IFigure)children.get(i);
+        child.setBounds(r);
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/SubordinateUpdateManager.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.SubordinateUpdateManager;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.UpdateManager;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.GraphicsSource;
+
+/**
+ * @deprecated this class is not used
+ */
+public abstract class SubordinateUpdateManager
+    : UpdateManager
+{
+
+/**
+ * A root figure.
+ */
+protected IFigure root;
+
+/**
+ * A graphics source
+ */
+protected GraphicsSource graphicsSource;
+
+/**
+ * @see UpdateManager#addDirtyRegion(IFigure, int, int, int, int)
+ */
+public void addDirtyRegion(IFigure f, int x, int y, int w, int h) {
+    if (getSuperior() is null)
+        return;
+    getSuperior().addDirtyRegion(f, x, y, w, h);
+}
+
+/**
+ * @see UpdateManager#addInvalidFigure(IFigure)
+ */
+public void addInvalidFigure(IFigure f) {
+    UpdateManager um = getSuperior();
+    if (um is null)
+        return;
+    um.addInvalidFigure(f);
+}
+
+/**
+ * Returns the host figure.
+ * @return the host figure
+ */
+protected abstract IFigure getHost();
+
+/**
+ * Returns the superior update manager.
+ * @return the superior
+ */
+protected UpdateManager getSuperior() {
+    if (getHost().getParent() is null)
+        return null;
+    return getHost().getParent().getUpdateManager();
+}
+
+/**
+ * @see UpdateManager#performUpdate()
+ */
+public void performUpdate() {
+    UpdateManager um = getSuperior();
+    if (um is null)
+        return;
+    um.performUpdate();
+}
+
+/**
+ * @see UpdateManager#performUpdate(Rectangle)
+ */
+public void performUpdate(Rectangle rect) {
+    UpdateManager um = getSuperior();
+    if (um is null)
+        return;
+    um.performUpdate(rect);
+}
+
+/**
+ * @see UpdateManager#setRoot(IFigure)
+ */
+public void setRoot(IFigure f) {
+    root = f;
+}
+
+/**
+ * @see UpdateManager#setGraphicsSource(GraphicsSource)
+ */
+public void setGraphicsSource(GraphicsSource gs) {
+    graphicsSource = gs;
+}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/TextUtilities.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.TextUtilities;
+
+import dwt.dwthelper.utils;
+
+
+import dwt.graphics.Font;
+import dwt.graphics.FontMetrics;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.FigureUtilities;
+
+/**
+ * Provides miscellaneous text operations. Clients may subclass this class if
+ * necessary.
+ *
+ * @author crevells
+ * @since 3.4
+ */
+public class TextUtilities {
+
+    /**
+     * a singleton default instance
+     */
+    public static TextUtilities INSTANCE;
+
+    static this(){
+        INSTANCE = new TextUtilities();
+    }
+
+    /**
+     * Returns the Dimensions of <i>s</i> in Font <i>f</i>.
+     *
+     * @param s
+     *            the string
+     * @param f
+     *            the font
+     * @return the dimensions of the given string
+     */
+    public Dimension getStringExtents(String s, Font f) {
+        return FigureUtilities.getStringExtents(s, f);
+    }
+
+    /**
+     * Returns the Dimensions of the given text, converting newlines and tabs
+     * appropriately.
+     *
+     * @param s
+     *            the text
+     * @param f
+     *            the font
+     * @return the dimensions of the given text
+     */
+    public Dimension getTextExtents(String s, Font f) {
+        return FigureUtilities.getTextExtents(s, f);
+    }
+
+    /**
+     * Gets the font's ascent.
+     *
+     * @param font
+     * @return the font's ascent
+     */
+    public int getAscent(Font font) {
+        FontMetrics fm = FigureUtilities.getFontMetrics(font);
+        return fm.getHeight() - fm.getDescent();
+    }
+
+    /**
+     * Gets the font's descent.
+     *
+     * @param font
+     * @return the font's descent
+     */
+    public int getDescent(Font font) {
+        return FigureUtilities.getFontMetrics(font).getDescent();
+    }
+
+    /**
+     * Returns the largest substring of <i>s</i> in Font <i>f</i> that can be
+     * confined to the number of pixels in <i>availableWidth<i>.
+     *
+     * @param s
+     *            the original string
+     * @param f
+     *            the font
+     * @param availableWidth
+     *            the available width
+     * @return the largest substring that fits in the given width
+     */
+    public int getLargestSubstringConfinedTo(String s, Font f,
+            int availableWidth) {
+        FontMetrics metrics = FigureUtilities.getFontMetrics(f);
+        int min, max;
+        float avg = metrics.getAverageCharWidth();
+        min = 0;
+        max = s.length + 1;
+
+        // The size of the current guess
+        int guess = 0, guessSize = 0;
+        while ((max - min) > 1) {
+            // Pick a new guess size
+            // New guess is the last guess plus the missing width in pixels
+            // divided by the average character size in pixels
+            guess = guess + cast(int) ((availableWidth - guessSize) / avg);
+
+            if (guess >= max)
+                guess = max - 1;
+            if (guess <= min)
+                guess = min + 1;
+
+            // Measure the current guess
+            guessSize = getTextExtents(s.substring(0, guess), f).width;
+
+            if (guessSize < availableWidth)
+                // We did not use the available width
+                min = guess;
+            else
+                // We exceeded the available width
+                max = guess;
+        }
+        return min;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/TitleBarBorder.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,198 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.TitleBarBorder;
+
+import dwt.dwthelper.utils;
+
+
+
+import dwt.graphics.Color;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.AbstractLabeledBorder;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.ColorConstants;
+import dwtx.draw2d.PositionConstants;
+
+/**
+ * Border provides a title bar on the Figure for which this is the border of. Generally
+ * used in conjunction with other borders to create window-like effects. Also provides for
+ * alignment of the text in the bar.
+ *
+ * @see FrameBorder
+ */
+public class TitleBarBorder
+    : AbstractLabeledBorder
+{
+
+private static Color defaultColor;
+
+private int textAlignment;
+private Insets padding;
+private Color fillColor;
+
+static this(){
+    defaultColor = ColorConstants.menuBackgroundSelected;
+}
+
+/**
+ * Constructs a TitleBarBorder with its label set to the name of this class.
+ *
+ * @since 2.0
+ */
+public this() {
+    textAlignment = PositionConstants.LEFT;
+    padding = new Insets(1, 3, 2, 2);
+    fillColor = defaultColor;
+}
+
+/**
+ * Constructs a TitleBarBorder with its label set to the passed String.
+ *
+ * @param s text of the label
+ * @since 2.0
+ */
+public this(String s) {
+    this();
+    setLabel(s);
+}
+
+/**
+ * Calculates and returns the Insets for this border.
+ *
+ * @param figure the figure on which Insets calculations are based
+ * @return the calculated Insets
+ * @since 2.0
+ */
+protected Insets calculateInsets(IFigure figure) {
+    return new Insets(getTextExtents(figure).height + padding.getHeight(), 0, 0, 0);
+}
+
+/**
+ * Returns the background Color of this TitleBarBorder.
+ * @return the background color
+ * @since 2.0
+ */
+protected Color getBackgroundColor() {
+    return fillColor;
+}
+
+/**
+ * Returns this TitleBarBorder's padding. Padding provides spacing along the sides of the
+ * TitleBarBorder. The default value is no padding along all sides.
+ *
+ * @return the Insets representing the space along the sides of the TitleBarBorder
+ * @since 2.0
+ */
+protected Insets getPadding() {
+    return padding;
+}
+
+/**
+ * Returns the alignment of the text in the title bar. Possible values are
+ * {@link PositionConstants#LEFT}, {@link PositionConstants#CENTER} and
+ * {@link PositionConstants#RIGHT}.
+ *
+ * @return the text alignment
+ * @since 2.0
+ */
+public int getTextAlignment() {
+    return textAlignment;
+}
+
+/**
+ * Returns <code>true</code> thereby filling up all the contents within its boundaries,
+ * eleminating the need by the figure to clip the boundaries and do the same.
+ *
+ * @see Border#isOpaque()
+ */
+public bool isOpaque() {
+    return true;
+}
+
+/**
+ * @see Border#paint(IFigure, Graphics, Insets)
+ */
+public void paint(IFigure figure, Graphics g, Insets insets) {
+    tempRect.setBounds(getPaintRectangle(figure, insets));
+    Rectangle rec = tempRect;
+    rec.height = Math.min(rec.height, getTextExtents(figure).height + padding.getHeight());
+    g.clipRect(rec);
+    g.setBackgroundColor(fillColor);
+    g.fillRectangle(rec);
+
+    int x = rec.x + padding.left;
+    int y = rec.y + padding.top;
+
+    int textWidth = getTextExtents(figure).width;
+    int freeSpace = rec.width - padding.getWidth() - textWidth;
+
+    if (getTextAlignment() is PositionConstants.CENTER)
+        freeSpace /= 2;
+    if (getTextAlignment() !is PositionConstants.LEFT)
+        x += freeSpace;
+
+    g.setFont(getFont(figure));
+    g.setForegroundColor(getTextColor());
+    g.drawString(getLabel(), x, y);
+}
+
+/**
+ * Sets the background color of the area within the boundaries of this border. This is
+ * required as this border takes responsibility for filling up the region, as
+ * TitleBarBorders are always opaque.
+ *
+ * @param color the background color
+ * @since 2.0
+ */
+public void setBackgroundColor(Color color) {
+    fillColor = color;
+}
+
+/**
+ * Sets the padding space to be applied on all sides of the border. The default value is
+ * no padding on all sides.
+ *
+ * @param all the value of the padding on all sides
+ * @since 2.0
+ */
+public void setPadding(int all) {
+    padding = new Insets(all);
+    invalidate();
+}
+
+/**
+ * Sets the padding space of this TitleBarBorder to the passed value. The default value is
+ * no padding on all sides.
+ *
+ * @param pad the padding
+ * @since 2.0
+ */
+public void setPadding(Insets pad) {
+    padding = pad; invalidate();
+}
+
+/**
+ * Sets the alignment of the text in the title bar. Possible values are
+ * {@link PositionConstants#LEFT}, {@link PositionConstants#CENTER} and
+ * {@link PositionConstants#RIGHT}.
+ *
+ * @param align the new text alignment
+ * @since 2.0
+ */
+public void setTextAlignment(int align_) {
+    textAlignment = align_;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Toggle.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.Toggle;
+
+import dwt.dwthelper.utils;
+
+import dwt.graphics.Image;
+import dwtx.draw2d.Clickable;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Label;
+
+/**
+ * Basic Rule for Toggle: Whoever creates the toggle is reponsible for response changes
+ * for it (selection, rollover, etc). Only {@link dwtx.draw2d.CheckBox} does its
+ * own listening.
+ */
+public class Toggle
+    : Clickable
+{
+
+/**
+ * Constructs a Toggle with no text or icon.
+ *
+ * @since 2.0
+ */
+public this() {
+    super();
+    setStyle(STYLE_TOGGLE);
+}
+
+/**
+ * Constructs a Toggle with passed text and icon
+ *
+ * @param text the text
+ * @param icon the icon
+ * @since 2.0
+ */
+public this(String text, Image icon) {
+    super(new Label(text, icon), STYLE_TOGGLE);
+}
+
+/**
+ * Constructs a Toggle with passed IFigure as its contents.
+ *
+ * @param contents the contents
+ * @since 2.0
+ */
+public this(IFigure contents) {
+    super(contents, STYLE_TOGGLE);
+}
+
+/**
+ * Constructs a Toggle with the passed figure as its contents and the given style.
+ * @param contents the contents
+ * @param style the style
+ */
+public this(IFigure contents, int style) {
+    super(contents, style);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ToggleButton.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ToggleButton;
+
+import dwt.dwthelper.utils;
+
+
+
+import dwt.graphics.Image;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.Toggle;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.Label;
+import dwtx.draw2d.ColorConstants;
+
+/**
+ * A Toggle that appears like a 3-dimensional button.
+ */
+public class ToggleButton
+    : Toggle
+{
+
+/**  This ToggleButton's Label **/
+protected Label label = null;
+
+/**
+ * Constructs a new ToggleButton with no initial contents.
+ */
+public this() {
+    setStyle(STYLE_BUTTON | STYLE_TOGGLE);
+}
+
+/**
+ * Constructs a ToggleButton with the passed IFigure as its contents.
+ *
+ * @param contents the contents of the toggle button
+ * @since 2.0
+ */
+public this(IFigure contents) {
+    super(contents, STYLE_BUTTON | STYLE_TOGGLE);
+}
+
+/**
+ * Constructs a ToggleButton with the passed string as its text.
+ *
+ * @param text the text to be displayed on the button
+ * @since 2.0
+ */
+public this(String text) {
+    this(text, null);
+}
+
+/**
+ * Constructs a ToggleButton with a Label containing the passed text and icon.
+ *
+ * @param text the text
+ * @param normalIcon the icon
+ * @since 2.0
+ */
+public this(String text, Image normalIcon) {
+    super(new Label(text, normalIcon), STYLE_BUTTON | STYLE_TOGGLE);
+}
+
+/**
+ * @see dwtx.draw2d.Figure#paintFigure(Graphics)
+ */
+protected void paintFigure(Graphics graphics) {
+    if (isSelected() && isOpaque()) {
+        fillCheckeredRectangle(graphics);
+    } else {
+        super.paintFigure(graphics);
+    }
+}
+
+/**
+ * Draws a checkered pattern to emulate a toggle button that is in the selected state.
+ * @param graphics  The Graphics object used to paint
+ */
+protected void fillCheckeredRectangle(Graphics graphics) {
+    graphics.setBackgroundColor(ColorConstants.button);
+    graphics.setForegroundColor(ColorConstants.buttonLightest);
+    Rectangle rect = getClientArea(Rectangle.SINGLETON).crop(new Insets(1, 1, 0, 0));
+    graphics.fillRectangle(rect.x, rect.y, rect.width, rect.height);
+
+    graphics.clipRect(rect);
+    graphics.translate(rect.x, rect.y);
+    int n = rect.width + rect.height;
+    for (int i = 1; i < n; i += 2) {
+        graphics.drawLine(0, i, i, 0);
+    }
+    graphics.restoreState();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ToggleModel.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ToggleModel;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.ButtonModel;
+
+/**
+ * ButtonModel that supports toggle buttons.
+ */
+public class ToggleModel
+    : ButtonModel
+{
+
+/**
+ * Notifies any ActionListeners on this ButtonModel that an action has been performed.
+ * Sets this ButtonModel's selection to be the opposite of what it was.
+ *
+ * @since 2.0
+ */
+public void fireActionPerformed() {
+    setSelected(!isSelected());
+    super.fireActionPerformed();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ToolTipHelper.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,174 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ToolTipHelper;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+import dwtx.dwtxhelper.Timer;
+import dwtx.dwtxhelper.TimerTask;
+
+import dwt.DWT;
+import dwt.events.MouseTrackAdapter;
+import dwt.graphics.Point;
+import dwt.widgets.Display;
+static import dwt.widgets.Control;
+static import dwt.graphics.Rectangle;
+static import dwt.events.MouseEvent;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.PopUpHelper;
+import dwtx.draw2d.ColorConstants;
+
+/**
+ * This class is used by SWTEventDispatcher as support to display Figure tooltips on a
+ * mouse hover event. Tooltips are drawn directly below the cursor unless the display
+ * does not allow, in which case the tooltip will be drawn directly above the cursor.
+ * Tooltips will be displayed with a LineBorder. The background of the tooltips will be
+ * the standard DWT tooltipBackground color unless the Figure's tooltip has set its own
+ * background.
+ */
+public class ToolTipHelper
+    : PopUpHelper
+{
+
+private Timer timer;
+private IFigure currentTipSource;
+
+/**
+ * Constructs a ToolTipHelper to be associated with Control <i>c</i>.
+ *
+ * @param c the control
+ * @since 2.0
+ */
+public this(dwt.widgets.Control.Control c) {
+    super(c, DWT.TOOL | DWT.ON_TOP);
+    getShell().setBackground(ColorConstants.tooltipBackground);
+    getShell().setForeground(ColorConstants.tooltipForeground);
+}
+
+/*
+ * Calculates the location where the tooltip will be painted. Returns this as a Point.
+ * Tooltip will be painted directly below the cursor if possible, otherwise it will be
+ * painted directly above cursor.
+ */
+private Point computeWindowLocation(IFigure tip, int eventX, int eventY) {
+    dwt.graphics.Rectangle.Rectangle clientArea = control.getDisplay().getClientArea();
+    Point preferredLocation = new Point(eventX, eventY + 26);
+
+    Dimension tipSize = getLightweightSystem()
+        .getRootFigure()
+        .getPreferredSize()
+        .getExpanded(getShellTrimSize());
+
+    // Adjust location if tip is going to fall outside display
+    if (preferredLocation.y + tipSize.height > clientArea.height)
+        preferredLocation.y = eventY - tipSize.height;
+
+    if (preferredLocation.x + tipSize.width > clientArea.width)
+        preferredLocation.x -= (preferredLocation.x + tipSize.width) - clientArea.width;
+
+    return preferredLocation;
+}
+
+/**
+ * Sets the LightWeightSystem's contents to the passed tooltip, and displays the tip. The
+ * tip will be displayed only if the tip source is different than the previously viewed
+ * tip source. (i.e. The cursor has moved off of the previous tooltip source figure.)
+ * <p>
+ * The tooltip will be painted directly below the cursor if possible, otherwise it will be
+ * painted directly above cursor.
+ *
+ * @param hoverSource the figure over which the hover event was fired
+ * @param tip the tooltip to be displayed
+ * @param eventX the x coordinate of the hover event
+ * @param eventY the y coordinate of the hover event
+ * @since 2.0
+ */
+public void displayToolTipNear(IFigure hoverSource, IFigure tip, int eventX, int eventY) {
+    if (tip !is null && hoverSource !is currentTipSource) {
+        getLightweightSystem().setContents(tip);
+        Point displayPoint = computeWindowLocation(tip, eventX, eventY);
+        Dimension shellSize = getLightweightSystem().getRootFigure()
+            .getPreferredSize().getExpanded(getShellTrimSize());
+        setShellBounds(displayPoint.x, displayPoint.y, shellSize.width, shellSize.height);
+        show();
+        currentTipSource = hoverSource;
+        timer = new Timer(true);
+        timer.schedule(new class() TimerTask {
+            public void run() {
+                Display.getDefault().syncExec(dgRunnable( {
+                    hide();
+                    timer.cancel();
+                }));
+            }
+        }, 5000);
+    }
+}
+
+/**
+ * Disposes of the tooltip's shell and kills the timer.
+ *
+ * @see PopUpHelper#dispose()
+ */
+public void dispose() {
+    if (isShowing()) {
+        timer.cancel();
+        hide();
+    }
+    getShell().dispose();
+}
+
+/**
+ * @see PopUpHelper#hookShellListeners()
+ */
+protected void hookShellListeners() {
+    // Close the tooltip window if the mouse enters the tooltip
+    getShell().addMouseTrackListener(new class() MouseTrackAdapter {
+        public void mouseEnter(dwt.events.MouseEvent.MouseEvent e) {
+            hide();
+            currentTipSource = null;
+            timer.cancel();
+        }
+    });
+}
+
+/**
+ * Displays the hover source's tooltip if a tooltip of another source is currently being
+ * displayed.
+ *
+ * @param figureUnderMouse the figure over which the cursor was when called
+ * @param tip the tooltip to be displayed
+ * @param eventX the x coordinate of the cursor
+ * @param eventY the y coordinate of the cursor
+ * @since 2.0
+ */
+public void updateToolTip(IFigure figureUnderMouse, IFigure tip, int eventX, int eventY) {
+    /* If the cursor is not on any Figures, it has been moved
+       off of the control. Hide the tool tip. */
+    if (figureUnderMouse is null) {
+        if (isShowing()) {
+            hide();
+            timer.cancel();
+        }
+    }
+    // Makes tooltip appear without a hover event if a tip is currently being displayed
+    if (isShowing() && figureUnderMouse !is currentTipSource) {
+        hide();
+        timer.cancel();
+        displayToolTipNear(figureUnderMouse, tip, eventX, eventY);
+    } else if (!isShowing() && figureUnderMouse !is currentTipSource)
+        currentTipSource = null;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ToolbarLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,428 @@
+/*******************************************************************************
+ * 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ToolbarLayout;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.geometry.Transposer;
+import dwtx.draw2d.AbstractHintLayout;
+import dwtx.draw2d.IFigure;
+
+/**
+ * Arranges figures in a single row or column.  Orientation can be set to produce either
+ * a row or column layout. This layout tries to fit all children within the parent's
+ * client area.  To do this, it compresses the children by some amount, but will not
+ * compress them smaller than their minimum size. If a child's preferred size is smaller
+ * than the row's or column's minor dimension, the layout can be configured to stretch the
+ * child.
+ */
+public class ToolbarLayout
+    : AbstractHintLayout
+{
+
+/** Space in pixels between Figures **/
+protected int spacing;
+/** Sets whether children should "stretch" with their container **/
+protected bool matchWidth;
+/** Orientation of layout **/
+protected bool horizontal = false;
+/** Alignment of layout **/
+protected int minorAlignment;
+
+/** Constant for center alignment **/
+public static const int ALIGN_CENTER = 0;
+/** Constant for top-left alignment **/
+public static const int ALIGN_TOPLEFT = 1;
+/** Constant for bottom-right alignment **/
+public static const int ALIGN_BOTTOMRIGHT = 2;
+
+/** Constant for horizontal alignment **/
+public static const bool HORIZONTAL = true;
+/** Constant for vertical alignment **/
+public static const bool VERTICAL = false;
+
+/** Transposer object used in layout calculations **/
+protected Transposer transposer;
+private void instanceInit() {
+    transposer = new Transposer();
+    transposer.setEnabled(horizontal);
+}
+
+/**
+ * Constructs a vertically oriented ToolbarLayout with child spacing of 0 pixels,
+ * matchWidth <code>true</code>, and {@link #ALIGN_TOPLEFT} alignment.
+ *
+ * @since 2.0
+ */
+public this() {
+    instanceInit();
+    spacing = 0;
+    matchWidth = true;
+    minorAlignment = ALIGN_TOPLEFT;
+    horizontal = false;
+}
+
+/**
+ * Constructs a ToolbarLayout with a specified orientation. Default values are: child
+ * spacing 0 pixels, matchWidth <code>false</code>, and {@link #ALIGN_TOPLEFT}
+ * alignment.
+ *
+ * @param isHorizontal whether the children are oriented horizontally
+ * @since 2.0
+ */
+public this(bool isHorizontal) {
+    instanceInit();
+    horizontal = isHorizontal;
+    transposer.setEnabled(horizontal);
+    spacing = 0;
+    matchWidth = false;
+    minorAlignment = ALIGN_TOPLEFT;
+}
+
+private Dimension calculateChildrenSize(List children, int wHint, int hHint,
+            bool preferred) {
+    Dimension childSize;
+    IFigure child;
+    int height = 0, width = 0;
+    for (int i = 0; i < children.size(); i++) {
+        child = cast(IFigure)children.get(i);
+        childSize = transposer.t(preferred ? getChildPreferredSize(child, wHint, hHint)
+                                           : getChildMinimumSize(child, wHint, hHint));
+        height += childSize.height;
+        width = Math.max(width, childSize.width);
+    }
+    return new Dimension(width, height);
+}
+
+/**
+ * Calculates the minimum size of the container based on the given hints. If this is a
+ * vertically-oriented Toolbar Layout, then only the widthHint is respected (which means
+ * that the children can be as tall as they desire).   In this case, the minimum width
+ * is that of the widest child, and the minimum height is the sum of the minimum
+ * heights of all children, plus the spacing between them. The border and insets of the
+ * container figure are also accounted for.
+ *
+ * @param container the figure whose minimum size has to be calculated
+ * @param wHint the width hint (the desired width of the container)
+ * @param hHint the height hint (the desired height of the container)
+ * @return the minimum size of the container
+ * @see #getMinimumSize(IFigure, int, int)
+ * @since 2.1
+ */
+protected Dimension calculateMinimumSize(IFigure container, int wHint, int hHint) {
+    Insets insets = container.getInsets();
+    if (isHorizontal()) {
+        wHint = -1;
+        if (hHint >= 0)
+            hHint = Math.max(0, hHint - insets.getHeight());
+    } else {
+        hHint = -1;
+        if (wHint >= 0)
+            wHint = Math.max(0, wHint - insets.getWidth());
+    }
+
+    List children = container.getChildren();
+    Dimension minSize = calculateChildrenSize(children, wHint, hHint, false);
+    // Do a second pass, if necessary
+    if (wHint >= 0 && minSize.width > wHint) {
+        minSize = calculateChildrenSize(children, minSize.width, hHint, false);
+    } else if (hHint >= 0 && minSize.width > hHint) {
+        minSize = calculateChildrenSize(children, wHint, minSize.width, false);
+    }
+
+    minSize.height += Math.max(0, children.size() - 1) * spacing;
+    return transposer.t(minSize)
+            .expand(insets.getWidth(), insets.getHeight())
+            .union_(getBorderPreferredSize(container));
+}
+
+/**
+ * Calculates the preferred size of the container based on the given hints. If this is a
+ * vertically-oriented Toolbar Layout, then only the widthHint is respected (which means
+ * that the children can be as tall as they desire).   In this case, the preferred width
+ * is that of the widest child, and the preferred height is the sum of the preferred
+ * heights of all children, plus the spacing between them.  The border and insets of the
+ * container figure are also accounted for.
+ *
+ * @param container the figure whose preferred size has to be calculated
+ * @param wHint the width hint (the desired width of the container)
+ * @param hHint the height hint (the desired height of the container)
+ * @return the preferred size of the container
+ * @see #getPreferredSize(IFigure, int, int)
+ * @since 2.0
+ */
+protected Dimension calculatePreferredSize(IFigure container, int wHint, int hHint) {
+    Insets insets = container.getInsets();
+    if (isHorizontal()) {
+        wHint = -1;
+        if (hHint >= 0)
+            hHint = Math.max(0, hHint - insets.getHeight());
+    } else {
+        hHint = -1;
+        if (wHint >= 0)
+            wHint = Math.max(0, wHint - insets.getWidth());
+    }
+
+    List children = container.getChildren();
+    Dimension prefSize = calculateChildrenSize(children, wHint, hHint, true);
+    // Do a second pass, if necessary
+    if (wHint >= 0 && prefSize.width > wHint) {
+        prefSize = calculateChildrenSize(children, prefSize.width, hHint, true);
+    } else if (hHint >= 0 && prefSize.width > hHint) {
+        prefSize = calculateChildrenSize(children, wHint, prefSize.width, true);
+    }
+
+    prefSize.height += Math.max(0, children.size() - 1) * spacing;
+    return transposer.t(prefSize)
+            .expand(insets.getWidth(), insets.getHeight())
+            .union_(getBorderPreferredSize(container));
+}
+
+/**
+ * @param child the figure whose minimum size is to be determined
+ * @param wHint the width hint
+ * @param hHint the height hint
+ * @return the given figure's minimum size
+ * @since 3.3
+ */
+protected Dimension getChildMinimumSize(IFigure child, int wHint, int hHint) {
+    return child.getMinimumSize(wHint, hHint);
+}
+
+/**
+ * @param child the figure whose preferred size is to be determined
+ * @param wHint the width hint
+ * @param hHint the height hint
+ * @return given figure's preferred size
+ * @since 3.3
+ */
+protected Dimension getChildPreferredSize(IFigure child, int wHint, int hHint) {
+    return child.getPreferredSize(wHint, hHint);
+}
+
+/**
+ * Returns the minor aligment of the layout.  Minor minor axis is the axis perpindicular
+ * to the overall orientation set in the contructor.
+ * @return the minor aligment
+ */
+public int getMinorAlignment() {
+    return minorAlignment;
+}
+
+/**
+ * @return the spacing between children
+ */
+public int getSpacing() {
+    return spacing;
+}
+
+/**
+ * Returns <code>true</code> if stretch minor axis has been enabled. The default value is
+ * false.
+ * @return <code>true</code> if stretch minor axis is enabled
+ */
+public bool getStretchMinorAxis() {
+    return matchWidth;
+}
+
+/**
+ * @return whether the orientation of the layout is horizontal
+ * @since 2.0
+ */
+public bool isHorizontal() {
+    return horizontal;
+}
+
+/**
+ * @see dwtx.draw2d.AbstractHintLayout#isSensitiveHorizontally(IFigure)
+ */
+protected bool isSensitiveHorizontally(IFigure parent) {
+    return !isHorizontal();
+}
+
+/**
+ * @see dwtx.draw2d.AbstractHintLayout#isSensitiveVertically(IFigure)
+ */
+protected bool isSensitiveVertically(IFigure parent) {
+    return isHorizontal();
+}
+
+/**
+ * @see dwtx.draw2d.LayoutManager#layout(IFigure)
+ */
+public void layout(IFigure parent) {
+    List children = parent.getChildren();
+    int numChildren = children.size();
+    Rectangle clientArea = transposer.t(parent.getClientArea());
+    int x = clientArea.x;
+    int y = clientArea.y;
+    int availableHeight = clientArea.height;
+
+    Dimension prefSizes [] = new Dimension[numChildren];
+    Dimension minSizes [] = new Dimension[numChildren];
+
+    // Calculate the width and height hints.  If it's a vertical ToolBarLayout,
+    // then ignore the height hint (set it to -1); otherwise, ignore the
+    // width hint.  These hints will be passed to the children of the parent
+    // figure when getting their preferred size.
+    int wHint = -1;
+    int hHint = -1;
+    if (isHorizontal()) {
+        hHint = parent.getClientArea(Rectangle.SINGLETON).height;
+    } else {
+        wHint = parent.getClientArea(Rectangle.SINGLETON).width;
+    }
+
+    /*
+     * Calculate sum of preferred heights of all children(totalHeight).
+     * Calculate sum of minimum heights of all children(minHeight).
+     * Cache Preferred Sizes and Minimum Sizes of all children.
+     *
+     * totalHeight is the sum of the preferred heights of all children
+     * totalMinHeight is the sum of the minimum heights of all children
+     * prefMinSumHeight is the sum of the difference between all children's
+     * preferred heights and minimum heights. (This is used as a ratio to
+     * calculate how much each child will shrink).
+     */
+    IFigure child;
+    int totalHeight = 0;
+    int totalMinHeight = 0;
+    int prefMinSumHeight = 0;
+
+    for (int i = 0; i < numChildren; i++) {
+        child = cast(IFigure)children.get(i);
+
+        prefSizes[i] = transposer.t(getChildPreferredSize(child, wHint, hHint));
+        minSizes[i] = transposer.t(getChildMinimumSize(child, wHint, hHint));
+
+        totalHeight += prefSizes[i].height;
+        totalMinHeight += minSizes[i].height;
+    }
+    totalHeight += (numChildren - 1) * spacing;
+    totalMinHeight += (numChildren - 1) * spacing;
+    prefMinSumHeight = totalHeight - totalMinHeight;
+    /*
+     * The total amount that the children must be shrunk is the
+     * sum of the preferred Heights of the children minus
+     * Max(the available area and the sum of the minimum heights of the children).
+     *
+     * amntShrinkHeight is the combined amount that the children must shrink
+     * amntShrinkCurrentHeight is the amount each child will shrink respectively
+     */
+    int amntShrinkHeight = totalHeight - Math.max(availableHeight, totalMinHeight);
+
+    if (amntShrinkHeight < 0) {
+        amntShrinkHeight = 0;
+    }
+
+    for (int i = 0; i < numChildren; i++) {
+        int amntShrinkCurrentHeight = 0;
+        int prefHeight = prefSizes[i].height;
+        int minHeight = minSizes[i].height;
+        int prefWidth = prefSizes[i].width;
+        int minWidth = minSizes[i].width;
+        Rectangle newBounds = new Rectangle(x, y, prefWidth, prefHeight);
+
+        child = cast(IFigure)children.get(i);
+        if (prefMinSumHeight !is 0)
+            amntShrinkCurrentHeight =
+                    (prefHeight - minHeight) * amntShrinkHeight / (prefMinSumHeight);
+
+        int width = Math.min(prefWidth, transposer.t(child.getMaximumSize()).width);
+        if (matchWidth)
+            width = transposer.t(child.getMaximumSize()).width;
+        width = Math.max(minWidth, Math.min(clientArea.width, width));
+        newBounds.width = width;
+
+          int adjust = clientArea.width - width;
+        switch (minorAlignment) {
+        case ALIGN_TOPLEFT:
+            adjust = 0;
+            break;
+        case ALIGN_CENTER:
+            adjust /= 2;
+            break;
+        case ALIGN_BOTTOMRIGHT:
+            break;
+        }
+        newBounds.x += adjust;
+        newBounds.height -= amntShrinkCurrentHeight;
+        child.setBounds(transposer.t(newBounds));
+
+        amntShrinkHeight -= amntShrinkCurrentHeight;
+        prefMinSumHeight -= (prefHeight - minHeight);
+        y += newBounds.height + spacing;
+    }
+}
+
+/**
+ * Sets the alignment of the children contained in the layout.  Possible values are
+ * {@link #ALIGN_CENTER}, {@link #ALIGN_BOTTOMRIGHT} and {@link #ALIGN_TOPLEFT}.
+ *
+ * @param align the minor alignment
+ * @since 2.0
+ */
+public void setMinorAlignment(int align_) {
+    minorAlignment = align_;
+}
+
+/**
+ * Sets the amount of space between children.
+ *
+ * @param space the amount of space between children
+ * @since 2.0
+ */
+public void setSpacing(int space) {
+    spacing = space;
+}
+
+/**
+ * Sets children's width (if vertically oriented) or height (if horizontally oriented) to
+ * stretch with their container.
+ *
+ * @deprecated use {@link #setStretchMinorAxis(bool)}
+ * @param match whether to stretch children
+ * @since 2.0
+ */
+public void setMatchWidth(bool match) {
+    matchWidth = match;
+}
+
+/**
+ * Causes children that are smaller in the dimension of the minor axis to be stretched to
+ * fill the minor axis.  The minor axis is the opposite of the orientation.
+ * @param stretch whether to stretch children
+ * @since 2.0
+ */
+public void setStretchMinorAxis(bool stretch) {
+    matchWidth = stretch;
+}
+
+/**
+ * Sets the orientation of the children in the ToolbarLayout.
+ *
+ * @param flag whether the orientation should be vertical
+ * @since 2.0
+ */
+public void setVertical(bool flag) {
+    if (horizontal !is flag) return;
+    invalidate();
+    horizontal = !flag;
+    transposer.setEnabled(horizontal);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/TreeSearch.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.TreeSearch;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.IFigure;
+
+/**
+ * A helper used in depth-first searches of a figure subgraph.
+ * @author hudsonr
+ * @since 2.1
+ */
+public interface TreeSearch {
+
+/**
+ * Returns <code>true</code> if the given figure is accepted by the search.
+ * @param figure the current figure in the traversal
+ * @return  <code>true</code> if the figure is accepted
+ */
+bool accept(IFigure figure);
+
+/**
+ * Returns <code>true</code> if the figure and all of its contained figures should be
+ * pruned from the search.
+ * @param figure the current figure in the traversal
+ * @return  <code>true</code> if the subgraph should be pruned
+ */
+bool prune(IFigure figure);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Triangle.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,150 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.Triangle;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.Shape;
+import dwtx.draw2d.Orientable;
+import dwtx.draw2d.Graphics;
+
+/**
+ * A triangular graphical figure.
+ */
+public final class Triangle
+    : Shape
+    , Orientable
+{
+
+/** The direction this triangle will face. Possible values are
+ * {@link PositionConstants#NORTH}, {@link PositionConstants#SOUTH},
+ * {@link PositionConstants#EAST} and {@link PositionConstants#WEST}.
+ */
+protected int direction = NORTH;
+/** The orientation of this triangle.  Possible values are {@link Orientable#VERTICAL}
+ * and {@link Orientable#HORIZONTAL}.
+ */
+protected int orientation = VERTICAL;
+
+/** The points of the triangle. */
+protected PointList triangle;
+
+public this(){
+    triangle = new PointList(3);
+}
+/**
+ * @see Shape#fillShape(Graphics)
+ */
+protected void fillShape(Graphics g) {
+    g.fillPolygon(triangle);
+}
+
+/**
+ * @see Shape#outlineShape(Graphics)
+ */
+protected void outlineShape(Graphics g) {
+    g.drawPolygon(triangle);
+}
+
+/**
+ * @see Figure#primTranslate(int, int)
+ */
+public void primTranslate(int dx, int dy) {
+    super.primTranslate(dx, dy);
+    triangle.translate(dx, dy);
+}
+
+/**
+ * @see Orientable#setDirection(int)
+ */
+public void setDirection(int value) {
+    if ((value & (NORTH | SOUTH)) !is 0)
+        orientation = VERTICAL;
+    else
+        orientation = HORIZONTAL;
+    direction = value;
+    revalidate();
+    repaint();
+}
+
+/**
+ * @see Orientable#setOrientation(int)
+ */
+public void setOrientation(int value) {
+    if (orientation is VERTICAL && value is HORIZONTAL) {
+        if (direction is NORTH) setDirection(WEST);
+        else setDirection(EAST);
+    }
+    if (orientation is HORIZONTAL && value is VERTICAL) {
+        if (direction is WEST) setDirection(NORTH);
+        else setDirection(SOUTH);
+    }
+}
+
+/**
+ * @see IFigure#validate()
+ */
+public void validate() {
+    super.validate();
+    Rectangle r = new Rectangle();
+    r.setBounds(getBounds());
+    r.crop(getInsets());
+    r.resize(-1, -1);
+    int size;
+    switch (direction & (NORTH | SOUTH)) {
+        case 0: //East or west.
+            size = Math.min(r.height / 2, r.width);
+            r.x += (r.width - size) / 2;
+            break;
+        default: //North or south
+            size = Math.min(r.height, r.width / 2);
+            r.y += (r.height - size) / 2;
+            break;
+    }
+
+    size = Math.max(size, 1); //Size cannot be negative
+
+    Point head, p2, p3;
+
+    switch (direction) {
+        case NORTH:
+            head = new Point(r.x + r.width / 2, r.y);
+            p2   = new Point (head.x - size, head.y + size);
+            p3   = new Point (head.x + size, head.y + size);
+            break;
+        case SOUTH:
+            head = new Point (r.x + r.width / 2, r.y + size);
+            p2   = new Point (head.x - size, head.y - size);
+            p3   = new Point (head.x + size, head.y - size);
+            break;
+        case WEST:
+            head = new Point (r.x, r.y + r.height / 2);
+            p2   = new Point (head.x + size, head.y - size);
+            p3   = new Point (head.x + size, head.y + size);
+            break;
+        default:
+            head = new Point(r.x + size, r.y + r.height / 2);
+            p2   = new Point(head.x - size, head.y - size);
+            p3   = new Point(head.x - size, head.y + size);
+
+    }
+    triangle.removeAllPoints();
+    triangle.addPoint(head);
+    triangle.addPoint(p2);
+    triangle.addPoint(p3);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/UpdateListener.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.UpdateListener;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Rectangle;
+
+/**
+ * An interface used to notify listeners that the listened to object is updating.
+ */
+public interface UpdateListener {
+
+/**
+ * Notifies the listener that the listened to object is painting.  The damage rectangle
+ * may be null or empty.  This indicates the dirty regions were clipped or not visible.
+ * But for objects such as the {@link dwtx.draw2d.parts.Thumbnail}, notification
+ * still needs to occur.  The map of dirty regions is passed to allow the listener to
+ * determine if it needs to update, for instance when a particular figure is painting.
+ *
+ * @param damage The area being painted
+ * @param dirtyRegions a Map of figures to their dirty regions
+ */
+void notifyPainting(Rectangle damage, Map dirtyRegions);
+
+/**
+ * Notifies the listener that the listened to object is validating.
+ */
+void notifyValidating();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/UpdateManager.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,202 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.UpdateManager;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+import dwtx.dwtxhelper.Collection;
+
+import dwt.graphics.GC;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.UpdateListener;
+import dwtx.draw2d.GraphicsSource;
+
+/**
+ * Update managers handle the job of laying out and repainting figures. A desirable
+ * implementation is to batches work to be done and collapses any redundant work. For
+ * example, clients may be making multiple changes to figures, which require laying out
+ * the same container or repainting the same region.
+ * <P>
+ * The update manager receives requests to validate certain figures, and repaint certain
+ * areas of figures. An update manager could process every request synchronously, or it
+ * could batch these requests and process them asynchronously.
+ * <P>
+ * The update process occurs in two phases. The first phase is laying out invalid figures.
+ * This phase comes first because it usually introduces additional damage regions. In some
+ * cases, while validating figures, new invalid figures may be appended to the update
+ * manager. Of course, damage regions will be reported too as figures are layed out.
+ * <P>
+ * The second phase is to repaint all damaged areas. The update manager will typically
+ * batch, clip, and union, all rectangles and perform a single paint of the overall
+ * damaged area.
+ *
+ */
+public abstract class UpdateManager {
+
+private UpdateListener listeners[];
+private bool disposed;
+
+/**
+ * Adds the dirty region defined by the coordinates on the IFigure <b>figure</b>.  The
+ * update manager should repaint the dirty region in a timely fashion.
+ *
+ * @param figure the dirty figure
+ * @param x the x coordinate of the dirty region
+ * @param y the y coordinate of the dirty region
+ * @param w the width of the dirty region
+ * @param h the height of the dirty region
+ */
+public abstract void addDirtyRegion(IFigure figure, int x, int y, int w, int h);
+
+/**
+ * @see #addDirtyRegion(IFigure, int, int, int, int)
+ */
+public void addDirtyRegion(IFigure figure, Rectangle rect) {
+    addDirtyRegion(figure, rect.x, rect.y, rect.width, rect.height);
+}
+
+/**
+ * Causes an update to occur at some time, and the given runnable to be executed
+ * following the update.
+ * @since 3.1
+ * @param run the runnable
+ */
+public void runWithUpdate(Runnable run) { }
+
+/**
+ * The receiver should call validate() on the IFigure <i>figure</i> in a timely fashion.
+ *
+ * @param figure the invalid figure
+ */
+public abstract void addInvalidFigure(IFigure figure);
+
+/**
+ * Adds the given listener to the list of listeners to be notified of painting and
+ * validation.
+ * @param listener the listener to add
+ */
+public void addUpdateListener(UpdateListener listener) {
+    if (listener is null)
+        throw new IllegalArgumentException("");
+    if (listeners is null) {
+        listeners = new UpdateListener[1];
+        listeners[0] = listener;
+    } else {
+        int oldSize = listeners.length;
+        UpdateListener newListeners[] = new UpdateListener[oldSize + 1];
+        SimpleType!(UpdateListener).arraycopy(listeners, 0, newListeners, 0, oldSize);
+        newListeners[oldSize] = listener;
+        listeners = newListeners;
+    }
+}
+
+/**
+ * Called when the EditPartViewer is being disposed.
+ */
+public void dispose() {
+    disposed = true;
+}
+
+/**
+ * Notifies listeners that painting is about to occur, passing them the damaged rectangle
+ * and the map of dirty regions.
+ * @param damage the damaged rectangle
+ * @param dirtyRegions map of dirty regions to figures
+ */
+protected void firePainting(Rectangle damage, Map dirtyRegions) {
+    UpdateListener localListeners[] = listeners;
+    for (int i = 0; i < localListeners.length; i++)
+        localListeners[i].notifyPainting(damage, dirtyRegions);
+}
+
+/**
+ * Notifies listeners that validation is about to occur.
+ */
+protected void fireValidating() {
+    UpdateListener localListeners[] = listeners;
+    for (int i = 0; i < localListeners.length; i++)
+        localListeners[i].notifyValidating();
+}
+
+/**
+ * @return whether this update manager has been disposed.
+ */
+protected bool isDisposed() {
+    return disposed;
+}
+
+/**
+ * Forces an update to occur. Update managers will perform updates automatically, but may
+ * do so asynchronously. Calling this method forces a synchronous update.
+ */
+public abstract void performUpdate();
+
+void paint(GC gc) {
+    performUpdate(new Rectangle(gc.getClipping()));
+}
+
+/**
+ * Performs an update on the given exposed rectangle.
+ * @param exposed the exposed rectangle
+ */
+public abstract void performUpdate(Rectangle exposed);
+
+/**
+ * Removes one occurrence of the given UpdateListener by identity.
+ * @param listener the listener to remove
+ */
+public void removeUpdateListener(UpdateListener listener) {
+    if (listener is null)
+        throw new IllegalArgumentException("");
+    for (int index = 0; index < listeners.length; index++)
+        if (listeners[index] is listener) {
+            int newSize = listeners.length - 1;
+            UpdateListener newListeners[] = null;
+            if (newSize !is 0) {
+                newListeners = new UpdateListener[newSize];
+                SimpleType!(UpdateListener).arraycopy(listeners, 0, newListeners, 0, index);
+                SimpleType!(UpdateListener).arraycopy(listeners, index + 1, newListeners, index, newSize - index);
+            } else {
+                newListeners = new UpdateListener[0];
+            }
+            listeners = newListeners;
+            return;
+        }
+}
+
+/**
+ * Sets the GraphicsSource for this update manager.
+ * @param gs the new GraphicsSource
+ */
+public abstract void setGraphicsSource(GraphicsSource gs);
+
+/**
+ * Sets the root figure.
+ * @param figure the new root figure
+ */
+public abstract void setRoot(IFigure figure);
+
+/**
+ * Performs a partial update if supported (validation only). Fires notification to
+ * listeners that validation has been performed. By default this method calls {@link
+ * #performUpdate()}. Subclasses should override this method to support validation
+ * without repainting.
+ *
+ * @since 3.2
+ */
+public void performValidation() {
+    performUpdate();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/Viewport.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,380 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.Viewport;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Bean;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.geometry.Translatable;
+import dwtx.draw2d.Figure;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.RangeModel;
+import dwtx.draw2d.ViewportLayout;
+import dwtx.draw2d.DefaultRangeModel;
+
+/**
+ * A Viewport is a flexible window onto a {@link ScrollPane} and represents the visible
+ * portion of the ScrollPane.
+ */
+public class Viewport
+    : Figure
+    , PropertyChangeListener
+{
+
+public alias Figure.getClientArea getClientArea;
+
+/** ID for the view location property */
+public static final String PROPERTY_VIEW_LOCATION = "viewLocation"; //$NON-NLS-1$
+private IFigure view;
+
+private bool useTranslate = false;
+private bool trackWidth = false;
+private bool trackHeight = false;
+private bool ignoreScroll = false;
+private RangeModel
+    horiztonalRangeModel = null,
+    verticalRangeModel = null;
+
+private void instanceInit(){
+    setLayoutManager(new ViewportLayout());
+    setHorizontalRangeModel(new DefaultRangeModel());
+    setVerticalRangeModel(new DefaultRangeModel());
+}
+
+/**
+ * Constructs a new Viewport with the default values.
+ */
+public this() {
+    instanceInit();
+}
+
+/**
+ * Constructs a new Viewport.  If <i>setting</i> is <code>true</code>, the viewport will
+ * use graphics translation to paint.
+ * @param setting whether to use graphics translation
+ */
+public this(bool setting) {
+    instanceInit();
+    useTranslate = setting;
+}
+
+/**
+ * @see IFigure#getClientArea(Rectangle)
+ */
+public Rectangle getClientArea(Rectangle rect) {
+    super.getClientArea(rect);
+    if (useGraphicsTranslate())
+        rect.translate(getViewLocation());
+    return rect;
+}
+
+/**
+ * Returns the view, which is the contents of the {@link ScrollPane} associated with this
+ * Viewport.
+ *
+ * @return the contents
+ * @since 2.0
+ */
+public IFigure getContents() {
+    return view;
+}
+
+/**
+ * Returns the RangeModel associated with the horizontal motion of this Viewport
+ * @return the RangeModel
+ *
+ * @since 2.0
+ */
+public RangeModel getHorizontalRangeModel() {
+    return horiztonalRangeModel;
+}
+
+/**
+ * Returns <code>true</code> if the Viewport resizes itself in the vertical direction when
+ * the  available height of its view is decreased, false otherwise. This option is turned
+ * off by default, and can be activated by calling
+ * {@link #setContentsTracksHeight(bool)} and passing in <code>true</code>.
+ *
+ * @return whether the contents tracks height
+ * @since 2.0
+ */
+public bool getContentsTracksHeight() {
+    return trackHeight;
+}
+
+/**
+ * Returns <code>true</code> if the Viewport resizes itself in the horizontal direction
+ * when the available width of its view is decreased, false otherwise. This option is
+ * turned off by default, and can be activated by calling
+ * {@link #setContentsTracksWidth(bool)} and passing in <code>true</code>.
+ *
+ * @return whether the contents tracks width
+ * @since 2.0
+ */
+public bool getContentsTracksWidth() {
+    return trackWidth;
+}
+
+/**
+ * Returns the range model associated with the vertical motion of the Viewport.
+ * @return the RangeModel
+ *
+ * @since 2.0
+ */
+public RangeModel getVerticalRangeModel() {
+    return verticalRangeModel;
+}
+
+/**
+ * Returns the current location of this Viewport.
+ * @return the current location of this Viewport
+ *
+ * @since 2.0
+ */
+public Point getViewLocation() {
+    return new Point(
+        getHorizontalRangeModel().getValue(),
+        getVerticalRangeModel().getValue());
+}
+
+private void localRevalidate() {
+    invalidate();
+    if (getLayoutManager() !is null)
+        getLayoutManager().invalidate();
+    getUpdateManager().addInvalidFigure(this);
+}
+
+/**
+ * @see Figure#paintClientArea(Graphics)
+ */
+protected void paintClientArea(Graphics g) {
+    if (useGraphicsTranslate()) {
+        Point p = getViewLocation();
+        try {
+            g.translate(-p.x, -p.y);
+            g.pushState();
+            super.paintClientArea(g);
+            g.popState();
+        } finally {
+            g.translate(p.x, p.y);
+        }
+    } else
+        super.paintClientArea(g);
+}
+
+/**
+ * @see dwtx.draw2d.Figure#isCoordinateSystem()
+ */
+public bool isCoordinateSystem() {
+    return useGraphicsTranslate() || super.isCoordinateSystem();
+}
+
+/**
+ * Listens for either of the {@link RangeModel RangeModels} to fire a property change
+ * event and updates the view accordingly.
+ * @param event the event
+ */
+public void propertyChange(PropertyChangeEvent event) {
+    if (null !is cast(RangeModel)(event.getSource()) ) {
+        if (RangeModel.PROPERTY_VALUE.equals(event.getPropertyName())) {
+            if (!ignoreScroll) {
+                localRevalidate();
+                if (useGraphicsTranslate()) {
+                    repaint();
+                    fireMoved();
+                }
+            }
+            firePropertyChange(PROPERTY_VIEW_LOCATION, event.getOldValue(),
+                                event.getNewValue());
+        }
+    }
+}
+
+/**
+ * Sets extents of {@link RangeModel RangeModels} to the client area of this Viewport.
+ * Sets RangeModel minimums to zero. Sets RangeModel maximums to this Viewport's
+ * height/width.
+ *
+ * @since 2.0
+ */
+protected void readjustScrollBars() {
+    if (getContents() is null)
+        return;
+    getVerticalRangeModel().setAll(0, getClientArea().height,
+                                    getContents().getBounds().height);
+    getHorizontalRangeModel().setAll(0, getClientArea().width,
+                                    getContents().getBounds().width);
+}
+
+/**
+ * Sets this Viewport to be associated with the passed Figure.
+ *
+ * @param figure the new contents
+ * @since 2.0
+ */
+public void setContents(IFigure figure) {
+    if (view is figure)
+        return;
+    if (view !is null)
+        remove(view);
+    view = figure;
+    if (view !is null)
+        add(figure);
+}
+
+/**
+ * Toggles the Viewport's ability to resize itself automatically when its view is
+ * decreased in size in the vertical direction. This is disabled by default.
+ *
+ * @param track whether this viewport should track its height
+ * @since 2.0
+ */
+public void setContentsTracksHeight(bool track) {
+    trackHeight = track;
+}
+
+/**
+ * Toggles the Viewport's ability to resize itself automatically when its view is
+ * decreased in size in the horizontal direction. This is disabled by default.
+ *
+ * @param track whether this viewport should track its width
+ * @since 2.0
+ */
+public void setContentsTracksWidth(bool track) {
+    trackWidth = track;
+}
+
+/**
+ * Sets the horizontal location of the Viewport's view to the passed value.
+ *
+ * @param value the new horizontal location
+ * @since 2.0
+ */
+public void setHorizontalLocation(int value) {
+    setViewLocation(value, getVerticalRangeModel().getValue());
+}
+
+/**
+ * Sets the horizontal range model to the passed RangeModel.
+ *
+ * @param rangeModel the new horizontal range model
+ * @since 2.0
+ */
+public void setHorizontalRangeModel(RangeModel rangeModel) {
+    if (horiztonalRangeModel !is null)
+        horiztonalRangeModel.removePropertyChangeListener(this);
+    horiztonalRangeModel = rangeModel;
+    horiztonalRangeModel.addPropertyChangeListener(this);
+}
+
+/**
+ * If <i>value</i> is <code>true</code>, this viewport will ignore any scrolling that
+ * occurs until this method is called again with <code>false</code>.
+ *
+ * @param value whether this viewport should ignore future scrolls
+ */
+public void setIgnoreScroll(bool value) {
+    ignoreScroll = value;
+}
+
+/**
+ * Sets the vertical location of the Viewport's view to the passed value.
+ *
+ * @param value the new vertical location
+ * @since 2.0
+ */
+public void setVerticalLocation(int value) {
+    setViewLocation(getHorizontalRangeModel().getValue(), value);
+}
+
+/**
+ * Sets the vertical range model to the passed RangeModel.
+ *
+ * @param rangeModel the new vertical RangeModel
+ * @since 2.0
+ */
+public void setVerticalRangeModel(RangeModel rangeModel) {
+    if (verticalRangeModel !is null)
+        verticalRangeModel.removePropertyChangeListener(this);
+    verticalRangeModel = rangeModel;
+    verticalRangeModel.addPropertyChangeListener(this);
+}
+
+/**
+ * Sets the location of the Viewport's view to the passed values.
+ *
+ * @param x The new x coordinate of the Viewport's view.
+ * @param y The new y coordinate of the Viewport's view.
+ * @since 2.0
+ */
+public void setViewLocation(int x, int y) {
+    if (getHorizontalRangeModel().getValue() !is x)
+        getHorizontalRangeModel().setValue(x);
+    if (getVerticalRangeModel().getValue() !is y)
+        getVerticalRangeModel().setValue(y);
+}
+
+/**
+ * Sets the location of the Viewport's view to the passed Point.
+ *
+ * @param p The new location of the Viewport's view.
+ * @since 2.0
+ */
+public void setViewLocation(Point p) {
+    setViewLocation(p.x, p.y);
+}
+
+/**
+ * @see IFigure#translateFromParent(Translatable)
+ */
+public void translateFromParent(Translatable t) {
+    if (useTranslate)
+        t.performTranslate(
+            getHorizontalRangeModel().getValue(),
+            getVerticalRangeModel().getValue()
+        );
+    super.translateFromParent(t);
+}
+
+/**
+ * @see IFigure#translateToParent(Translatable)
+ */
+public void translateToParent(Translatable t) {
+    if (useTranslate)
+        t.performTranslate(
+            -getHorizontalRangeModel().getValue(),
+            -getVerticalRangeModel().getValue()
+        );
+    super.translateToParent(t);
+}
+
+/**
+ * Returns <code>true</code> if this viewport uses graphics translation.
+ * @return whether this viewport uses graphics translation
+ */
+public bool useGraphicsTranslate() {
+    return useTranslate;
+}
+
+/**
+ * @see IFigure#validate()
+ */
+public void validate() {
+    super.validate();
+    readjustScrollBars();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ViewportLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.ViewportLayout;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.AbstractHintLayout;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Viewport;
+
+/**
+ * Layout for a viewport. A viewport is a flexible window
+ * onto a figure.
+ */
+public class ViewportLayout
+    : AbstractHintLayout
+{
+
+/**
+ * Returns the minimum size required by the input viewport figure. Since viewport is
+ * flexible, the minimum size required would be the just the size of the borders.
+ * @see AbstractHintLayout#calculateMinimumSize(IFigure, int, int)
+ */
+protected Dimension calculateMinimumSize(IFigure figure, int wHint, int hHint) {
+    Viewport viewport = cast(Viewport)figure;
+    Dimension min = new Dimension();
+    Insets insets = viewport.getInsets();
+    return min.getExpanded(insets.getWidth(), insets.getHeight());
+}
+
+/**
+ * Calculates and returns the preferred size of the figure based on the given hints. The
+ * given wHint is ignored unless the viewport (parent) is tracking width. The same is true
+ * for the height hint.
+ * @param parent the Viewport whose preferred size is to be calculated
+ * @param wHint the width hint
+ * @param hHint the height hint
+ * @return the Preferred size of the given viewport
+ * @since   2.0
+ */
+protected Dimension calculatePreferredSize(IFigure parent, int wHint, int hHint) {
+    Viewport viewport = cast(Viewport)parent;
+    Insets insets = viewport.getInsets();
+    IFigure contents = viewport.getContents();
+
+    if (viewport.getContentsTracksWidth() && wHint > -1)
+        wHint = Math.max(0, wHint - insets.getWidth());
+    else
+        wHint = -1;
+    if (viewport.getContentsTracksHeight() && hHint > -1)
+        hHint = Math.max(0, hHint - insets.getHeight());
+    else
+        hHint = -1;
+
+    if (contents is null) {
+        return new Dimension(insets.getWidth(), insets.getHeight());
+    } else {
+        Dimension minSize = contents.getMinimumSize(wHint, hHint);
+        if (wHint > -1)
+            wHint = Math.max(wHint, minSize.width);
+        if (hHint > -1)
+            hHint = Math.max(hHint, minSize.height);
+        return contents
+            .getPreferredSize(wHint, hHint)
+            .getExpanded(insets.getWidth(), insets.getHeight());
+    }
+
+    //Layout currently does not union border's preferred size.
+}
+
+/**
+ * @see dwtx.draw2d.AbstractHintLayout#isSensitiveHorizontally(IFigure)
+ */
+protected bool isSensitiveHorizontally(IFigure parent) {
+    return (cast(Viewport)parent).getContentsTracksWidth();
+}
+
+/**
+ * @see dwtx.draw2d.AbstractHintLayout#isSensitiveHorizontally(IFigure)
+ */
+protected bool isSensitiveVertically(IFigure parent) {
+    return (cast(Viewport)parent).getContentsTracksHeight();
+}
+
+/**
+ * @see dwtx.draw2d.LayoutManager#layout(IFigure)
+ */
+public void layout(IFigure figure) {
+    Viewport viewport = cast(Viewport)figure;
+    IFigure contents = viewport.getContents();
+
+    if (contents is null) return;
+    Point p = viewport.getClientArea().getLocation();
+
+    p.translate(viewport.getViewLocation().getNegated());
+
+    // Calculate the hints
+    Rectangle hints = viewport.getClientArea();
+    int wHint = viewport.getContentsTracksWidth() ? hints.width : -1;
+    int hHint = viewport.getContentsTracksHeight() ? hints.height : -1;
+
+    Dimension newSize = viewport.getClientArea().getSize();
+    Dimension min = contents.getMinimumSize(wHint, hHint);
+    Dimension pref = contents.getPreferredSize(wHint, hHint);
+
+    if (viewport.getContentsTracksHeight())
+        newSize.height = Math.max(newSize.height, min.height);
+    else
+        newSize.height = Math.max(newSize.height, pref.height);
+
+    if (viewport.getContentsTracksWidth())
+        newSize.width = Math.max(newSize.width, min.width);
+    else
+        newSize.width = Math.max(newSize.width, pref.width);
+
+    contents.setBounds(new Rectangle(p, newSize));
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/XYAnchor.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.XYAnchor;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.ConnectionAnchorBase;
+import dwtx.draw2d.IFigure;
+
+/**
+ * Supports an anchor in the XY layout. This anchor exists independently without an owner.
+ */
+public class XYAnchor
+    : ConnectionAnchorBase
+{
+
+private Point location;
+
+/**
+ * Constructs an XYAnchor at the Point p.
+ *
+ * @param p the point where this anchor will be located
+ * @since 2.0
+ */
+public this(Point p) {
+    location = new Point(p);
+}
+
+/**
+ * Returns the location of this anchor relative to the reference point given in as input.
+ * Since this is XY layout, the location of the point is independent of the reference
+ * point.
+ *
+ * @see ConnectionAnchor#getLocation(Point)
+ */
+public Point getLocation(Point reference) {
+    return location;
+}
+
+/**
+ * Returns <code>null</code> as these anchors inherently do not depend on other figures
+ * for their location.
+ *
+ * @see ConnectionAnchor#getOwner()
+ * @since 2.0
+ */
+public IFigure getOwner() {
+    return null;
+}
+
+/**
+ * Returns the point which is used as the reference by this connection anchor. In the case
+ * of the XYAnchor, this point is the same as its location.
+ *
+ * @see ConnectionAnchor#getReferencePoint()
+ */
+public Point getReferencePoint() {
+    return location;
+}
+
+/**
+ * Sets the location of this anchor and notifies all the listeners of the update.
+ *
+ * @param p the new location of this anchor
+ * @see  #getLocation(Point)
+ * @since 2.0
+ */
+public void setLocation(Point p) {
+    location.setLocation(p);
+    fireAnchorMoved();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/XYLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.XYLayout;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Rectangle;
+
+import dwtx.draw2d.AbstractLayout;
+import dwtx.draw2d.IFigure;
+
+/**
+ * This class implements the {@link dwtx.draw2d.LayoutManager} interface using the
+ * XY Layout algorithm. This lays out the components using the layout constraints as
+ * defined by each component.
+ */
+public class XYLayout
+    : AbstractLayout
+{
+
+/** The layout contraints */
+protected Map constraints;
+
+this(){
+    constraints = new HashMap();
+}
+
+/**
+ * Calculates and returns the preferred size of the input figure. Since in XYLayout the
+ * location of the child should be preserved, the preferred size would be a region which
+ * would hold all the children of the input figure. If no constraint is set, that child
+ * is ignored for calculation. If width and height are not positive, the preferred
+ * dimensions of the child are taken.
+ *
+ * @see AbstractLayout#calculatePreferredSize(IFigure, int, int)
+ * @since 2.0
+ */
+protected Dimension calculatePreferredSize(IFigure f, int wHint, int hHint) {
+    Rectangle rect = new Rectangle();
+    ListIterator children = f.getChildren().listIterator();
+    while (children.hasNext()) {
+        IFigure child = cast(IFigure)children.next();
+        Rectangle r = cast(Rectangle)constraints.get(cast(Object)child);
+        if (r is null)
+            continue;
+
+        if (r.width is -1 || r.height is -1) {
+            Dimension preferredSize = child.getPreferredSize(r.width, r.height);
+            r = r.getCopy();
+            if (r.width is -1)
+                r.width = preferredSize.width;
+            if (r.height is -1)
+                r.height = preferredSize.height;
+        }
+        rect.union_(r);
+    }
+    Dimension d = rect.getSize();
+    Insets insets = f.getInsets();
+    return (new Dimension(d.width + insets.getWidth(), d.height + insets.getHeight())).
+        union_(getBorderPreferredSize(f));
+}
+
+/**
+ * @see LayoutManager#getConstraint(IFigure)
+ */
+public Object getConstraint(IFigure figure) {
+    return constraints.get(cast(Object)figure);
+}
+
+/**
+ * Returns the origin for the given figure.
+ * @param parent the figure whose origin is requested
+ * @return the origin
+ */
+public Point getOrigin(IFigure parent) {
+    return parent.getClientArea().getLocation();
+}
+
+/**
+ * Implements the algorithm to layout the components of the given container figure.
+ * Each component is laid out using its own layout constraint specifying its size
+ * and position.
+ *
+ * @see LayoutManager#layout(IFigure)
+ */
+public void layout(IFigure parent) {
+    Iterator children = parent.getChildren().iterator();
+    Point offset = getOrigin(parent);
+    IFigure f;
+    while (children.hasNext()) {
+        f = cast(IFigure)children.next();
+        Rectangle bounds = cast(Rectangle)getConstraint(f);
+        if (bounds is null) continue;
+
+        if (bounds.width is -1 || bounds.height is -1) {
+            Dimension preferredSize = f.getPreferredSize(bounds.width, bounds.height);
+            bounds = bounds.getCopy();
+            if (bounds.width is -1)
+                bounds.width = preferredSize.width;
+            if (bounds.height is -1)
+                bounds.height = preferredSize.height;
+        }
+        bounds = bounds.getTranslated(offset);
+        f.setBounds(bounds);
+    }
+}
+
+/**
+ * @see LayoutManager#remove(IFigure)
+ */
+public void remove(IFigure figure) {
+    super.remove(figure);
+    constraints.remove(cast(Object)figure);
+}
+
+/**
+ * Sets the layout constraint of the given figure. The constraints can only be of type
+ * {@link Rectangle}.
+ *
+ * @see LayoutManager#setConstraint(IFigure, Object)
+ * @since 2.0
+ */
+public void setConstraint(IFigure figure, Object newConstraint) {
+    super.setConstraint(figure, newConstraint);
+    if (newConstraint !is null)
+        constraints.put(cast(Object)figure, newConstraint);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/geometry/Dimension.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,471 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.geometry.Dimension;
+
+import dwtx.draw2d.geometry.Point;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Translatable;
+static import dwt.graphics.Point;
+static import dwt.graphics.Image;
+static import dwt.graphics.Rectangle;
+import tango.text.convert.Format;
+
+/**
+ * Stores an integer width and height. This class provides various methods for
+ * manipulating this Dimension or creating new derived Objects.
+ */
+public class Dimension
+    : Cloneable/+, java.io.Serializable+/, Translatable
+{
+
+/**A singleton for use in short calculations.  Use to avoid newing unnecessary objects.*/
+public static const Dimension SINGLETON;
+
+static this(){
+    SINGLETON = new Dimension();
+}
+
+/**The width.*/
+public int width;
+/**The height. */
+public int height;
+
+static final long serialVersionUID = 1;
+
+/**
+ * Constructs a Dimension of zero width and height.
+ *
+ * @since 2.0
+ */
+public this() { }
+
+/**
+ * Constructs a Dimension with the width and height of the passed Dimension.
+ *
+ * @param d the Dimension supplying the initial values
+ * @since 2.0
+ */
+public this(Dimension d) {
+    width = d.width;
+    height = d.height;
+}
+
+/**
+ * Constructs a Dimension where the width and height are the x and y distances of the
+ * input point from the origin.
+ *
+ * @param pt the Point supplying the initial values
+ * @since 2.0
+ */
+public this(dwt.graphics.Point.Point pt) {
+    width = pt.x;
+    height = pt.y;
+}
+
+/**
+ * Constructs a Dimension with the supplied width and height values.
+ *
+ * @param w the width
+ * @param h the height
+ * @since 2.0
+ */
+public this(int w, int h) {
+    width = w;
+    height = h;
+}
+
+/**
+ * Constructs a Dimension with the width and height of the Image supplied as input.
+ *
+ * @param image the image supplying the dimensions
+ * @since 2.0
+ */
+public this(dwt.graphics.Image.Image image) {
+    dwt.graphics.Rectangle.Rectangle r = image.getBounds();
+    width = r.width;
+    height = r.height;
+}
+
+/**
+ * Returns <code>true</code> if the input Dimension fits into this Dimension. A Dimension
+ * of the same size is considered to "fit".
+ *
+ * @param d the dimension being tested
+ * @return  <code>true</code> if this Dimension contains <i>d</i>
+ * @since 2.0
+ */
+public bool contains(Dimension d) {
+    return width >= d.width && height >= d.height;
+}
+
+/**
+ * Returns <code>true</code> if this Dimension properly contains the one specified.
+ * Proper containment is defined as containment using "<", instead of "<=".
+ *
+ * @param d the dimension being tested
+ * @return <code>true</code> if this Dimension properly contains the one specified
+ * @since 2.0
+ */
+public bool containsProper(Dimension d) {
+    return width > d.width && height > d.height;
+}
+
+/**
+ * Copies the width and height values of the input Dimension to this Dimension.
+ *
+ * @param d the dimension supplying the values
+ * @since 2.0
+ */
+public void setSize(Dimension d) {
+    width = d.width;
+    height = d.height;
+}
+
+/**
+ * Returns the area of this Dimension.
+ *
+ * @return the area
+ * @since 2.0
+ */
+public int getArea() {
+    return width * height;
+}
+
+/**
+ * Creates and returns a copy of this Dimension.
+ * @return a copy of this Dimension
+ * @since 2.0
+ */
+public Dimension getCopy() {
+    return new Dimension(this);
+}
+
+/**
+ * Creates and returns a new Dimension representing the difference between this Dimension
+ * and the one specified.
+ *
+ * @param d the dimension being compared
+ * @return a new dimension representing the difference
+ * @since 2.0
+ */
+public Dimension getDifference(Dimension d) {
+    return new Dimension(width - d.width, height - d.height);
+}
+
+/**
+ * Creates and returns a Dimension representing the sum of this Dimension and the one
+ * specified.
+ *
+ * @param d the dimension providing the expansion width and height
+ * @return a new dimension expanded by <i>d</i>
+ * @since 2.0
+ */
+public Dimension getExpanded(Dimension d) {
+    return new Dimension(width + d.width, height + d.height);
+}
+
+/**
+ * Creates and returns a new Dimension representing the sum of this Dimension and the one
+ * specified.
+ *
+ * @param w value by which the width of this is to be expanded
+ * @param h value by which the height of this is to be expanded
+ * @return a new Dimension expanded by the given values
+ * @since 2.0
+ */
+public Dimension getExpanded(int w, int h) {
+    return new Dimension(width + w, height + h);
+}
+
+/**
+ * Creates and returns a new Dimension representing the intersection of this Dimension and
+ * the one specified.
+ *
+ * @param d the Dimension to intersect with
+ * @return A new Dimension representing the intersection
+ * @since 2.0
+ */
+public Dimension getIntersected(Dimension d) {
+    return (new Dimension(this)).intersect(d);
+}
+
+/**
+ * Creates and returns a new Dimension with negated values.
+ *
+ * @return  a new Dimension with negated values
+ * @since 2.0
+ */
+public Dimension getNegated() {
+    return new Dimension(0 - width, 0 - height);
+}
+
+/**
+ * Returns whether the input Object is equivalent to this Dimension. <code>true</code> if
+ * the Object is a Dimension and its width and height are equal to this Dimension's width
+ * and height, <code>false</code> otherwise.
+ *
+ * @param o the Object being tested for equality
+ * @return <code>true</code> if the given object is equal to this dimension
+ * @since 2.0
+ */
+public override int opEquals(Object o) {
+    if (auto d = cast(Dimension)o ) {
+        return (d.width is width && d.height is height);
+    }
+    return false;
+}
+
+/**
+ * Returns <code>true</code> if this Dimension's width and height are equal to the given
+ * width and height.
+ *
+ * @param w the width
+ * @param h the height
+ * @return <code>true</code> if this dimension's width and height are equal to those given.
+ * @since 2.0
+ */
+public bool equals(int w, int h) {
+    return width is w && height is h;
+}
+
+/**
+ * Expands the size of this Dimension by the specified amount.
+ *
+ * @param d the Dimension providing the expansion width and height
+ * @return <code>this</code> for convenience
+ * @since 2.0
+ */
+public Dimension expand(Dimension d) {
+    width  += d.width;
+    height += d.height;
+    return this;
+}
+
+/**
+ * Expands the size of this Dimension by the specified amound.
+ *
+ * @param pt the Point supplying the dimensional values
+ * @return <code>this</code> for convenience
+ * @since 2.0
+ */
+public Dimension expand(Point pt) {
+    width  += pt.x;
+    height += pt.y;
+    return this;
+}
+
+/**
+ * Expands the size of this Dimension by the specified width and height.
+ *
+ * @param w  Value by which the width should be increased
+ * @param h  Value by which the height should be increased
+ * @return <code>this</code> for convenience
+ * @since 2.0
+ */
+public Dimension expand(int w, int h) {
+    width  += w;
+    height += h;
+    return this;
+}
+
+/**
+ * Creates a new Dimension with its width and height scaled by the specified value.
+ *
+ * @param amount Value by which the width and height are scaled
+ * @return a new dimension with the scale applied
+ * @since 2.0
+ */
+public Dimension getScaled(double amount) {
+    return (new Dimension(this))
+        .scale(amount);
+}
+
+/**
+ * Creates a new Dimension with its height and width swapped. Useful in orientation change
+ * calculations.
+ *
+ * @return a new Dimension with its height and width swapped
+ * @since 2.0
+ */
+public Dimension getTransposed() {
+    return (new Dimension(this))
+        .transpose();
+}
+
+/**
+ * Creates a new Dimension representing the union of this Dimension with the one
+ * specified. Union is defined as the max() of the values from each Dimension.
+ *
+ * @param d the Dimension to be unioned
+ * @return a new Dimension
+ * @since 2.0
+ */
+public Dimension getUnioned(Dimension d) {
+    return (new Dimension(this)).union_(d);
+}
+
+/**
+ * @see java.lang.Object#toHash()
+ */
+public override hash_t toHash() {
+    return (width * height) ^ (width + height);
+}
+
+
+/**
+ * This Dimension is intersected with the one specified. Intersection is performed by
+ * taking the min() of the values from each dimension.
+ *
+ * @param d the Dimension used to perform the min()
+ * @return <code>this</code> for convenience
+ * @since 2.0
+ */
+public Dimension intersect(Dimension d) {
+    width = Math.min(d.width, width);
+    height = Math.min(d.height, height);
+    return this;
+}
+
+/**
+ * Returns <code>true</code> if the Dimension has width or height greater than 0.
+ *
+ * @return <code>true</code> if this Dimension is empty
+ * @since 2.0
+ */
+public bool isEmpty() {
+    return (width <= 0) || (height <= 0);
+}
+
+/**
+ * Negates the width and height of this Dimension.
+ *
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Dimension negate() {
+    width = 0 - width;
+    height = 0 - height;
+    return this;
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Translatable#performScale(double)
+ */
+public void performScale(double factor) {
+    scale(factor);
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Translatable#performTranslate(int, int)
+ */
+public void performTranslate(int dx, int dy) { }
+
+/**
+ * Scales the width and height of this Dimension by the amount supplied, and returns this
+ * for convenience.
+ *
+ * @param amount value by which this Dimension's width and height are to be scaled
+ * @return <code>this</code> for convenience
+ * @since 2.0
+ */
+public Dimension scale(double amount) {
+    return scale(amount, amount);
+}
+
+/**
+ * Scales the width of this Dimension by <i>w</i> and scales the height of this Dimension
+ * by <i>h</i>. Returns this for convenience.
+ *
+ * @param w the value by which the width is to be scaled
+ * @param h the value by which the height is to be scaled
+ * @return <code>this</code> for convenience
+ * @since 2.0
+ */
+public Dimension scale(double w, double h) {
+    width  = cast(int)(Math.floor(width * w));
+    height = cast(int)(Math.floor(height * h));
+    return this;
+}
+
+/**
+ * Reduces the width of this Dimension by <i>w</i>, and reduces the height of this
+ * Dimension by <i>h</i>. Returns this for convenience.
+ *
+ * @param w the value by which the width is to be reduced
+ * @param h the value by which the height is to be reduced
+ * @return <code>this</code> for convenience
+ * @since 2.0
+ */
+public Dimension shrink(int w, int h) {
+    return expand(-w, -h);
+}
+
+/**
+ * @see Object#toString()
+ */
+
+public override String toString() {
+    return Format("Dimension({}, {})", width, height);
+}
+
+/**
+ * Swaps the width and height of this Dimension, and returns this for convenience. Can be
+ * useful in orientation changes.
+ *
+ * @return <code>this</code> for convenience
+ * @since 2.0
+ */
+public Dimension transpose() {
+    int temp = width;
+    width = height;
+    height = temp;
+    return this;
+}
+
+/**
+ * Sets the width of this Dimension to the greater of this Dimension's width and
+ * <i>d</i>.width. Likewise for this Dimension's height.
+ *
+ * @param d the Dimension to union with this Dimension
+ * @return <code>this</code> for convenience
+ * @since 2.0
+ */
+public Dimension union_ (Dimension d) {
+    width = Math.max(width, d.width);
+    height = Math.max(height, d.height);
+    return this;
+}
+
+/**
+ * Returns <code>double</code> width
+ *
+ * @return <code>double</code> width
+ * @since 3.4
+ */
+public double preciseWidth() {
+    return width;
+}
+
+/**
+ * Returns <code>double</code> height
+ *
+ * @return <code>double</code> height
+ * @since 3.4
+ */
+public double preciseHeight() {
+    return height;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/geometry/Geometry.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.geometry.Geometry;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A Utilities class for geometry operations.
+ * @author Pratik Shah
+ * @since 3.1
+ */
+public class Geometry
+{
+
+/**
+ * Determines whether the two line segments formed by the given coordinates intersect.  If
+ * one of the two line segments starts or ends on the other line, then they are considered
+ * to be intersecting.
+ *
+ * @param ux x coordinate of starting point of line 1
+ * @param uy y coordinate of starting point of line 1
+ * @param vx x coordinate of ending point of line 1
+ * @param vy y coordinate of endpoing point of line 1
+ * @param sx x coordinate of the starting point of line 2
+ * @param sy y coordinate of the starting point of line 2
+ * @param tx x coordinate of the ending point of line 2
+ * @param ty y coordinate of the ending point of line 2
+ * @return <code>true</code> if the two line segments formed by the given coordinates
+ *         cross
+ * @since 3.1
+ */
+public static bool linesIntersect(int ux, int uy, int vx, int vy,
+        int sx, int sy, int tx, int ty) {
+    /*
+     * Given the segments: u-------v. s-------t. If s->t is inside the triangle u-v-s,
+     * then check whether the line u->u splits the line s->t.
+     */
+    /* Values are casted to long to avoid integer overflows */
+    long usX = cast(long) ux - sx;
+    long usY = cast(long) uy - sy;
+    long vsX = cast(long) vx - sx;
+    long vsY = cast(long) vy - sy;
+    long stX = cast(long) sx - tx;
+    long stY = cast(long) sy - ty;
+    if (productSign(cross(vsX, vsY, stX, stY), cross(stX, stY, usX, usY)) >= 0) {
+        long vuX = cast(long) vx - ux;
+        long vuY = cast(long) vy - uy;
+        long utX = cast(long) ux - tx;
+        long utY = cast(long) uy - ty;
+        return productSign(cross(-usX, -usY, vuX, vuY), cross(vuX, vuY, utX, utY)) <= 0;
+    }
+    return false;
+}
+
+private static int productSign(long x, long y) {
+    if (x is 0 || y is 0) {
+        return 0;
+    } else if (x < 0 ^ y < 0) {
+        return -1;
+    }
+    return 1;
+}
+
+private static long cross(long x1, long y1, long x2, long y2) {
+    return x1 * y2 - x2 * y1;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/geometry/Insets.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.geometry.Insets;
+
+import dwt.dwthelper.utils;
+import tango.text.convert.Format;
+
+/**
+ * Stores four integers for top, left, bottom, and right measurements.
+ */
+public class Insets
+    : Cloneable/+, java.io.Serializable+/
+{
+
+/** distance from left  */
+public int left;
+/** distance from top*/
+public int top;
+/** distance from bottom*/
+public int bottom;
+/** distance from right */
+public int right;
+
+static final long serialVersionUID = 1;
+
+/**
+ * Constructs an Insets with all zeroes.
+ * @since 2.0
+ */
+public this() { }
+
+/**
+ * Constructs a new Insets with initial values the same as the provided Insets.
+ * @param i  The insets to copy.
+ * @since 2.0
+ */
+public this(Insets i) {
+    this(i.top, i.left, i.bottom, i.right);
+}
+
+/**
+ * Constructs a new Insets with all the sides set to the speicifed value.
+ * @param i  Value applied to all sides of new Insets.
+ * @since 2.0
+ */
+public this(int i) {
+    this(i, i, i, i);
+}
+
+/**
+ * Creates a new Insets with the specified top, left, bottom, and right values.
+ * @param top  Value of the top space.
+ * @param left  Value of the left space.
+ * @param bottom  Value of the bottom space.
+ * @param right  Value of the right space.
+ * @since 2.0
+ */
+public this(int top, int left, int bottom, int right) {
+    this.top = top;
+    this.left = left;
+    this.bottom = bottom;
+    this.right = right;
+}
+
+/**
+ * Adds the values of the specified Insets to this Insets' values.
+ * @return <code>this</code> for convenience
+ * @param insets the Insets being added
+ * @since 2.0
+ */
+public Insets add(Insets insets) {
+    top    += insets.top;
+    bottom += insets.bottom;
+    left   += insets.left;
+    right  += insets.right;
+    return this;
+}
+
+/**
+ * Test for equality.
+ * The Insets are equal if their top, left, bottom, and
+ * right values are equivalent.
+ *
+ * @param o  Object being tested for equality.
+ * @return  true if all values are the same.
+ * @since 2.0
+ */
+public override int opEquals(Object o) {
+    if (auto i = cast(Insets)o ) {
+        return i.top is top
+            && i.bottom is bottom
+            && i.left is left
+            && i.right is right;
+    }
+    return false;
+}
+
+/**
+ * Creates an Insets representing the sum of this Insets with the specified Insets.
+ * @param insets  Insets to be added
+ * @return  A new Insets
+ * @since 2.0
+ */
+public Insets getAdded(Insets insets) {
+    return (new Insets(this)).add(insets);
+}
+
+/**
+ * Returns the height for this Insets, equal to <code>top</code> + <code>bottom</code>.
+ * @return The sum of top + bottom
+ * @see  #getWidth()
+ * @since 2.0
+ */
+public int getHeight() {
+    return top + bottom;
+}
+
+/**
+ * Creates a new Insets with transposed values.
+ * Top and Left are transposed.
+ * Bottom and Right are transposed.
+ * @return  New Insets with the transposed values.
+ * @since 2.0
+ */
+public Insets getTransposed() {
+    return (new Insets(this)).transpose();
+}
+
+/**
+ * Returns the width for this Insets, equal to <code>left</code> + <code>right</code>.
+ * @return The sum of left + right
+ * @see  #getHeight()
+ * @since 2.0
+ */
+public int getWidth() {
+    return left + right;
+}
+
+/**
+ * @see java.lang.Object#toHash()
+ */
+public override hash_t toHash() {
+    return top * 7 + left * 2 + bottom * 31 + right * 37;
+}
+
+
+/**
+ * Returns true if all values are 0.
+ * @return true if all values are 0
+ * @since 2.0
+ */
+public bool isEmpty() {
+    return (left is 0 && right is 0 && top is 0 && bottom is 0);
+}
+
+/**
+ * @return String representation.
+ * @since 2.0
+ */
+public override String toString() {
+    return Format( "Insets(t={}, l={}, b={}, r={})", top, left, bottom, right );
+}
+
+/**
+ * Transposes this object.  Top and Left are exchanged.  Bottom and Right are exchanged.
+ * Can be used in orientation changes.
+ * @return <code>this</code> for convenience
+ * @since 2.0
+ */
+public Insets transpose() {
+    int temp = top;
+    top = left;
+    left = temp;
+    temp = right;
+    right = bottom;
+    bottom = temp;
+    return this;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/geometry/Point.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,438 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.geometry.Point;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.PositionConstants;
+import dwtx.draw2d.geometry.Translatable;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Rectangle;
+
+static import dwt.graphics.Point;
+
+import tango.text.convert.Format;
+
+/**
+ * Represents a point (x, y) in 2-dimensional space.  This class provides various methods
+ * for manipulating this Point or creating new derived geometrical Objects.
+ */
+public class Point
+    : Cloneable, /+java.io.Serializable,+/ Translatable
+{
+
+static final long serialVersionUID = 1;
+/**A singleton for use in short calculations*/
+public static const Point SINGLETON;
+
+static this(){
+    SINGLETON = new Point();
+}
+
+/**x value*/
+public int x;
+/**y value*/
+public int y;
+
+/**
+ * Constructs a Point at location (0,0).
+ * @since 2.0
+ */
+public this() { }
+
+/**
+ * Constructs a Point at the same location as the given Point.
+ * @param copy  Point from which the initial values are taken.
+ * @since 2.0
+ */
+public this(Point copy) {
+    x = copy.x;
+    y = copy.y;
+}
+
+/**
+ * Constructs a Point at the same location as the given DWT Point.
+ * @param copy  Point from which the initial values are taken.
+ * @since 2.0
+ */
+public this(dwt.graphics.Point.Point copy) {
+    x = copy.x;
+    y = copy.y;
+}
+
+
+/**
+ * Constructs a Point at the specified x and y locations.
+ *
+ * @param x x value
+ * @param y y value
+ * @since 2.0
+ */
+public this(int x, int y) {
+    this.x = x;
+    this.y = y;
+}
+
+/**
+ * Constructs a Point at the specified x and y locations.
+ * @param x  x value
+ * @param y  y value
+ * @since 2.0
+ */
+public this(double x, double y) {
+    this.x = cast(int)x;
+    this.y = cast(int)y;
+}
+
+/**
+ * Test for equality.
+ * @param o Object being tested for equality
+ * @return  true if both x and y values are equal
+ * @since 2.0
+ */
+public override int opEquals(Object o) {
+    if ( auto p = cast(Point)o ) {
+        return p.x is x && p.y is y;
+    }
+    return false;
+}
+
+/**
+ * @return a copy of this Point
+ * @since 2.0
+ */
+public Point getCopy() {
+    return new Point(x, y);
+}
+
+/**
+ * Calculates the difference in between this Point and the one specified.
+ * @param pt The Point being subtracted from this Point
+ * @return A new Dimension representing the difference
+ * @since 2.0
+ */
+public Dimension getDifference(Point pt) {
+    return new Dimension(this.x - pt.x, this.y - pt.y);
+}
+
+/**
+ * Calculates the distance from this Point to the one specified.
+ * @param pt The Point being compared to this
+ * @return The distance
+ * @since 2.0
+ */
+public double getDistance(Point pt) {
+    return Math.sqrt(getPreciseDistance2(pt));
+}
+
+/**
+ * Calculates the distance squared between this Point and the one specified. If
+ * the distance squared is larger than the maximum integer value, then
+ * <code>Integer.MAX_VALUE</code> will be returned.
+ * @param pt The reference Point
+ * @return distance<sup>2</sup>
+ * @since 2.0
+ */
+public int getDistance2(Point pt) {
+    long i = pt.x - x;
+    long j = pt.y - y;
+    long result = i * i + j * j;
+    if (result > Integer.MAX_VALUE)
+        return Integer.MAX_VALUE;
+    return cast(int)result;
+}
+
+private double getPreciseDistance2(Point pt) {
+    double i = pt.preciseX() - preciseX();
+    double j = pt.preciseY() - preciseY();
+    return i * i + j * j;
+}
+
+/**
+ * Calculates the orthogonal distance to the specified point.  The orthogonal distance is
+ * the sum of the horizontal and vertical differences.
+ * @param pt The reference Point
+ * @return the orthoganal distance
+ */
+public int getDistanceOrthogonal(Point pt) {
+    return Math.abs(y - pt.y) + Math.abs(x - pt.x);
+}
+
+/**
+ * Creates a Point with negated x and y values.
+ * @return A new Point
+ * @since 2.0
+ */
+public Point getNegated() {
+    return getCopy().negate();
+}
+
+/**
+ * Calculates the relative position of the specified Point to this Point.
+ * @param p The reference Point
+ * @return NORTH, SOUTH, EAST, or WEST, as defined in {@link PositionConstants}
+ */
+public int getPosition(Point p) {
+    int dx = p.x - x;
+    int dy = p.y - y;
+    if (Math.abs(dx) > Math.abs(dy)) {
+        if (dx < 0)
+            return PositionConstants.WEST;
+        return PositionConstants.EAST;
+    }
+    if (dy < 0)
+        return PositionConstants.NORTH;
+    return PositionConstants.SOUTH;
+}
+
+/**
+ * Creates a new Point from this Point by scaling by the specified amount.
+ * @param amount scale factor
+ * @return A new Point
+ * @since 2.0
+ */
+public Point getScaled(double amount) {
+    return getCopy().scale(amount);
+}
+
+/**
+ * Creates a new DWT {@link dwt.graphics.Point Point} from this Point.
+ * @return  A new DWT Point
+ * @since 2.0
+ */
+public dwt.graphics.Point.Point getSWTPoint() {
+    return new dwt.graphics.Point.Point(x, y);
+}
+
+/**
+ * Creates a new Point which is translated by the values of the input Dimension.
+ * @param delta Dimension which provides the translation amounts.
+ * @return  A new Point
+ * @since 2.0
+ */
+public Point getTranslated(Dimension delta) {
+    return getCopy().translate(delta);
+}
+
+/**
+ * Creates a new Point which is translated by the specified x and y values
+ * @param x horizontal component
+ * @param y vertical component
+ * @return  A new Point
+ * @since 2.0
+ */
+public Point getTranslated(int x, int y) {
+    return getCopy().translate(x, y);
+}
+
+/**
+ * Creates a new Point which is translated by the values of the provided Point.
+ * @param pt Point which provides the translation amounts.
+ * @return  A new Point
+ * @since 2.0
+ */
+public Point getTranslated(Point pt) {
+    return getCopy().translate(pt);
+}
+
+/**
+ * Creates a new Point with the transposed values of this Point.
+ * Can be useful in orientation change calculations.
+ * @return  A new Point
+ * @since 2.0
+ */
+public Point getTransposed() {
+    return getCopy().transpose();
+}
+
+/**
+ * @see java.lang.Object#toHash()
+ */
+public override hash_t toHash() {
+    return (x * y) ^ (x + y);
+}
+
+/**
+ * Creates a new Point representing the MAX of two provided Points.
+ * @param p1 first point
+ * @param p2 second point
+ * @return A new Point representing the Max()
+ */
+public static Point max(Point p1, Point p2) {
+    return (new Rectangle(p1, p2))
+        .getBottomRight()
+        .translate(-1, -1);
+}
+
+/**
+ * Creates a new Point representing the MIN of two provided Points.
+ * @param p1 first point
+ * @param p2 second point
+ * @return A new Point representing the Min()
+ */
+public static Point min(Point p1, Point p2) {
+    return (new Rectangle(p1, p2)).getTopLeft();
+}
+
+/**
+ * Negates the x and y values of this Point.
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Point negate() {
+    x = -x;
+    y = -y;
+    return this;
+}
+
+/** @see Translatable#performScale(double) */
+public void performScale(double factor) {
+    scale(factor);
+}
+
+/** @see Translatable#performTranslate(int, int) */
+public void performTranslate(int dx, int dy) {
+    translate(dx, dy);
+}
+
+/**
+ * Scales this Point by the specified amount.
+ * @return  <code>this</code> for convenience
+ * @param amount scale factor
+ * @since 2.0
+ */
+public Point scale(double amount) {
+    x = cast(int)Math.floor(x * amount);
+    y = cast(int)Math.floor(y * amount);
+    return this;
+}
+
+/**
+ * Scales this Point by the specified values.
+ * @param xAmount horizontal scale factor
+ * @param yAmount  vertical scale factor
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Point scale(double xAmount, double yAmount) {
+    x = cast(int)Math.floor(x * xAmount);
+    y = cast(int)Math.floor(y * yAmount);
+    return this;
+}
+
+/**
+ * Sets the location of this Point to the provided x and y locations.
+ * @return  <code>this</code> for convenience
+ * @param x the x location
+ * @param y the y location
+ * @since 2.0
+ */
+public Point setLocation(int x, int y) {
+    this.x = x;
+    this.y = y;
+    return this;
+}
+
+/**
+ * Sets the location of this Point to the specified Point.
+ * @return  <code>this</code> for convenience
+ * @param pt the Location
+ * @since 2.0
+ */
+public Point setLocation(Point pt) {
+    x = pt.x;
+    y = pt.y;
+    return this;
+}
+
+/**
+ * @return String representation.
+ * @since 2.0
+ */
+public override String toString() {
+    return Format("Point({}, {})", x, y );//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$
+}
+
+/**
+ * Shifts the location of this Point by the location of the
+ * input Point along each of the axes, and returns this for
+ * convenience.
+ *
+ * @param p  Point to which the origin is being shifted.
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Point translate(Point p) {
+    return translate(p.x, p.y);
+}
+
+/**
+ * Shifts this Point by the values of the Dimension along
+ * each axis, and returns this for convenience.
+ *
+ * @param d  Dimension by which the origin is being shifted.
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Point translate(Dimension d) {
+    return translate(d.width, d.height);
+}
+
+/**
+ * Shifts this Point by the values supplied along each axes, and
+ * returns this for convenience.
+ *
+ * @param dx  Amount by which point is shifted along X axis.
+ * @param dy  Amount by which point is shifted along Y axis.
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Point translate(int dx, int dy) {
+    x += dx;
+    y += dy;
+    return this;
+}
+
+/**
+ * Transposes this object.  X and Y values are exchanged.
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Point transpose() {
+    int temp = x;
+    x = y;
+    y = temp;
+    return this;
+}
+
+/**
+ * Returns <code>double</code> x coordinate
+ *
+ * @return <code>double</code> x coordinate
+ * @since 3.4
+ */
+public double preciseX() {
+    return x;
+}
+
+/**
+ * Returns <code>double</code> y coordinate
+ *
+ * @return <code>double</code> y coordinate
+ * @since 3.4
+ */
+public double preciseY() {
+    return y;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/geometry/PointList.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,449 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.geometry.PointList;
+
+import dwt.dwthelper.utils;
+import tango.text.convert.Format;
+
+import dwtx.draw2d.geometry.Translatable;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Geometry;
+
+/**
+ * Represents a List of Points. This class is used for building an <code>int[]</code>.
+ * The array is internal, and is constructed and queried by the client using
+ * {@link Point Points}. DWT uses integer arrays when painting polylines and polygons.
+ */
+public class PointList
+    : /+java.io.Serializable,+/ Translatable
+{
+
+private int[] points;
+private Rectangle bounds;
+private int size_ = 0;
+
+static const long serialVersionUID = 1;
+
+/**
+ * Constructs an empty PointList.
+ *
+ * @since 2.0
+ */
+public this() { }
+
+/**
+ * Constructs a PointList with the given points.
+ * @param points int array where two consecutive ints form the coordinates of a point
+ * @since 3.1
+ */
+public this(int points[]) {
+    this.points = points;
+    this.size_ = points.length / 2;
+}
+
+/**
+ * Constructs a PointList with initial capacity <i>size</i>, but no points.
+ *
+ * @param size  Number of points to hold.
+ * @since 2.0
+ */
+public this(int size_) {
+    points = new int[size_ * 2];
+}
+
+/**
+ * Appends all of the given points to this PointList.
+ * @param source the source pointlist
+ */
+public void addAll(PointList source) {
+    ensureCapacity(size_ + source.size_);
+    System.arraycopy(source.points, 0, points, size_ * 2, source.size_ * 2);
+    size_ += source.size_;
+}
+
+/**
+ * Adds Point <i>p</i> to this PointList.
+ * @param p the point to be added
+ * @see  #removePoint(int)
+ * @since 2.0
+ */
+public void addPoint(Point p) {
+    addPoint(p.x, p.y);
+}
+
+/**
+ * Adds the input point values to this PointList.
+ * @param x  X value of a point to add
+ * @param y  Y value of a point to add
+ * @since 2.0
+ */
+public void addPoint(int x, int y) {
+    bounds = null;
+    int index = size_ * 2;
+    ensureCapacity(size_ + 1);
+    points[index] = x;
+    points[index + 1] = y;
+    size_++;
+}
+
+private void ensureCapacity(int newSize) {
+    newSize *= 2;
+    if (points.length < newSize) {
+        int old[] = points;
+        points = new int[Math.max(newSize, size_ * 4)];
+        System.arraycopy(old, 0, points, 0, size_ * 2);
+    }
+}
+
+/**
+ * Returns the smallest Rectangle which contains all Points.
+ * @return The smallest Rectangle which contains all Points.
+ * @since 2.0
+ */
+public Rectangle getBounds() {
+    if (bounds !is null)
+        return bounds;
+    bounds = new Rectangle();
+    if (size_ > 0) {
+        bounds.setLocation(getPoint(0));
+        for (int i = 0; i < size_; i++)
+            bounds.union_(getPoint(i));
+    }
+    return bounds;
+}
+
+/**
+ * Creates a copy
+ * @return PointList A copy of this PointList
+ */
+public PointList getCopy() {
+    PointList result = new PointList(size_);
+    System.arraycopy(points, 0, result.points, 0, size_ * 2);
+    result.size_ = size_;
+    result.bounds = null;
+    return result;
+}
+
+/**
+ * Returns the first Point in the list.
+ * @return  The first point in the list.
+ * @throws IndexOutOfBoundsException if the list is empty
+ * @since 2.0
+ */
+public Point getFirstPoint() {
+    return getPoint(0);
+}
+
+/**
+ * Returns the last point in the list.
+ * @throws IndexOutOfBoundsException if the list is empty
+ * @return  The last Point in the list
+ * @since 2.0
+ */
+public Point getLastPoint() {
+    return getPoint(size_ - 1);
+}
+
+/**
+ * Returns the midpoint of the list of Points. The midpoint is the median of the List,
+ * unless there are 2 medians (size is even), then the middle of the medians is returned.
+ * @return The midpoint
+ * @throws IndexOutOfBoundsException if the list is empty
+ */
+public Point getMidpoint() {
+    if (size() % 2 is 0)
+        return getPoint(size() / 2 - 1).
+            getTranslated(getPoint(size() / 2)).
+            scale(0.5f);
+    return getPoint(size() / 2);
+}
+
+/**
+ * Returns the Point in the list at the specified index.
+ * @param index Index of the desired Point
+ * @return  The requested Point
+ * @throws IndexOutOfBoundsException If the specified index is out of range
+ * @since 2.0
+ */
+public Point getPoint(int index) {
+    if (index < 0 || index >= size_)
+        throw new IndexOutOfBoundsException( Format(
+            "Index: {}, Size: {}", index, //$NON-NLS-1$
+            size_)); //$NON-NLS-1$
+    index *= 2;
+    return new Point(points[index], points[index + 1]);
+}
+
+/**
+ * Copies the x and y values at given index into a specified Point.
+ * This method exists to avoid the creation of a new <code>Point</code>.
+ * @see #getPoint(int)
+ * @param p The Point which will be set with the &lt;x, y&gt; values
+ * @param index The index being requested
+ * @return The parameter <code>p</code> is returned for convenience
+ * @since 2.0
+ */
+public Point getPoint(Point p, int index) {
+    if (index < 0 || index >= size_)
+        throw new IndexOutOfBoundsException( Format(
+            "Index: {}, Size: {}", index, //$NON-NLS-1$
+            size_)); //$NON-NLS-1$
+    index *= 2;
+    p.x = points[index];
+    p.y = points[index + 1];
+    return p;
+}
+
+/**
+ * Inserts a given point at a specified index.
+ * @param p  Point to be inserted.
+ * @param index  Position where the point is to be inserted.
+ * @exception IndexOutOfBoundsException  if the index is invalid
+ * @see  #setPoint(Point, int)
+ * @since 2.0
+ */
+public void insertPoint(Point p, int index) {
+    if (bounds !is null && !bounds.contains(p))
+        bounds = null;
+    if (index > size_ || index < 0)
+        throw new IndexOutOfBoundsException( Format(
+            "Index: {}, Size: {}", index, //$NON-NLS-1$
+            size_)); //$NON-NLS-1$
+    index *= 2;
+
+    int length = points.length;
+    int old[] = points;
+    points = new int[length + 2];
+    System.arraycopy(old, 0, points, 0, index);
+    System.arraycopy(old, index, points, index + 2, length - index);
+
+    points[index] = p.x;
+    points[index + 1] = p.y;
+    size_++;
+}
+
+/**
+ * Determines whether any of the line segments represented by this PointList intersect
+ * the given Rectangle.  If a segment touches the given rectangle, that's considered
+ * intersection.
+ *
+ * @param r the rectangle
+ * @return <code>true</code> if the given rectangle intersects any of the line segments
+ *         represented by this PointList
+ * @since 3.1
+ */
+public bool intersects(Rectangle r) {
+    if (r.isEmpty())
+        return false;
+    for (int i = 0; i < size_ * 2; i += 2) {
+        if (r.contains(points[i], points[i + 1]))
+            return true;
+    }
+    int diagonal1x1 = r.x,
+        diagonal1y1 = r.y,
+        diagonal1x2 = r.x + r.width - 1,
+        diagonal1y2 = r.y + r.height - 1,
+        diagonal2x1 = r.x + r.width - 1,
+        diagonal2y1 = r.y,
+        diagonal2x2 = r.x,
+        diagonal2y2 = r.y + r.height - 1;
+    for (int i = 0; i < (size_ - 1) * 2; i += 2) {
+        if (Geometry.linesIntersect(diagonal1x1, diagonal1y1, diagonal1x2,
+                diagonal1y2, points[i], points[i + 1], points[i + 2], points[i + 3])
+                || Geometry.linesIntersect(diagonal2x1, diagonal2y1, diagonal2x2,
+                diagonal2y2, points[i], points[i + 1], points[i + 2], points[i + 3]))
+            return true;
+    }
+    return false;
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Translatable#performScale(double)
+ */
+public void performScale(double factor) {
+    for (int i = 0; i < points.length; i++)
+        points[i] = cast(int)Math.floor(points[i] * factor);
+    bounds = null;
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Translatable#performTranslate(int, int)
+ */
+public void performTranslate(int dx, int dy) {
+    for (int i = 0; i < size_ * 2; i += 2) {
+        points[i] += dx;
+        points[i + 1] += dy;
+    }
+    if (bounds !is null)
+        bounds.translate(dx, dy);
+}
+
+/**
+ * Removes all the points stored by this list. Resets all
+ * the properties based on the point information.
+ *
+ * @since 2.0
+ */
+public void removeAllPoints() {
+    bounds = null;
+    size_ = 0;
+}
+
+/**
+ * Removes the point at the specified index from the PointList, and
+ * returns it.
+ * @since 2.0
+ * @see  #addPoint(Point)
+ * @param index   Index of the point to be removed.
+ * @return  The point which has been removed
+ * @throws IndexOutOfBoundsException if the removal index is beyond the list capacity
+ */
+public Point removePoint(int index) {
+    bounds = null;
+    if (index < 0 || index >= size_)
+        throw new IndexOutOfBoundsException( Format(
+            "Index: {}, Size: {}", index, //$NON-NLS-1$
+            size_)); //$NON-NLS-1$
+
+    index *= 2;
+    Point pt = new Point(points[index], points[index + 1]);
+    if (index !is size_ * 2 - 2)
+        System.arraycopy(points, index + 2, points, index, size_ * 2 - index - 2);
+    size_--;
+    return pt;
+}
+
+/**
+ * Reverses the order of the points in the list.
+ * @since 3.2
+ */
+public void reverse() {
+    int temp;
+    for (int i = 0, j = size_ * 2 - 2; i < size_; i += 2 , j -= 2) {
+        temp = points[i];
+        points[i] = points[j];
+        points[j] = temp;
+        temp = points[i + 1];
+        points[i + 1] = points[j + 1];
+        points[j + 1] = temp;
+    }
+}
+
+/**
+ * Overwrites a point at a given index in the list with the specified Point.
+ * @param pt  Point which is to be stored at the index.
+ * @param index  Index where the given point is to be stored.
+ * @since 2.0
+ */
+public void setPoint(Point pt, int index) {
+    if (index < 0 || index >= size_)
+        throw new IndexOutOfBoundsException( Format(
+            "Index: {}, Size: {}", index, //$NON-NLS-1$
+            size_)); //$NON-NLS-1$
+    if (bounds !is null && !bounds.contains(pt))
+        bounds = null;
+    points[index * 2] = pt.x;
+    points[index * 2 + 1] = pt.y;
+}
+
+/**
+ * Sets the size of this PointList.
+ * @param newSize the new size
+ */
+public void setSize(int newSize) {
+    if (points.length > newSize * 2) {
+        size_ = newSize;
+        return;
+    }
+    int[] newArray = new int[newSize * 2];
+    System.arraycopy(points, 0, newArray, 0, points.length);
+    points = newArray;
+    size_ = newSize;
+}
+
+/**
+ * Returns the number of points in this PointList.
+ * @return  The number of points
+ * @since 2.0
+ */
+public int size() {
+    return size_;
+}
+
+/**
+ * Returns the contents of this PointList as an integer array.  The returned array is by
+ * reference.  Any changes made to the array will also be changing the original PointList.
+ *
+ * @return the integer array of points by reference
+ * @since 2.0
+ */
+public int[] toIntArray() {
+    if (points.length !is size_ * 2) {
+        int[] old = points;
+        points = new int[size_ * 2];
+        System.arraycopy(old, 0, points, 0, size_ * 2);
+    }
+    return points;
+}
+
+/**
+ * Moves the origin (0,0) of the coordinate system of all
+ * the points to the Point <i>pt</i>. This updates the position
+ * of all the points in this PointList.
+ *
+ * @param pt  Position by which all the points will be shifted.
+ * @see #translate(int,int)
+ * @since 2.0
+ */
+public final void translate(Point pt) {
+    translate(pt.x, pt.y);
+}
+
+/**
+ * Moves the origin (0,0) of the coordinate system of all
+ * the points to the Point (x,y). This updates the position
+ * of all the points in this PointList.
+ *
+ * @param x  Amount by which all the points will be shifted on the X axis.
+ * @param y  Amount by which all the points will be shifted on the Y axis.
+ * @see  #translate(Point)
+ * @since 2.0
+ */
+public void translate(int x, int y) {
+    if (x is 0 && y is 0)
+        return;
+    if (bounds !is null)
+        bounds.translate(x, y);
+    for (int i = 0; i < size_ * 2; i += 2) {
+        points[i] += x;
+        points[i + 1] += y;
+    }
+}
+
+/**
+ * Transposes all x and y values. Useful for orientation changes.
+ * @since 3.2
+ */
+public void transpose() {
+    int temp;
+    if (bounds !is null)
+        bounds.transpose();
+    for (int i = 0; i < size_ * 2; i += 2) {
+        temp = points[i];
+        points[i] = points[i + 1];
+        points[i + 1] = temp;
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/geometry/PrecisionDimension.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.geometry.PrecisionDimension;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.geometry.Dimension;
+
+
+/**
+ * @author Randy Hudson
+ */
+public class PrecisionDimension : Dimension {
+
+/**
+ * The width in double precision.
+ */
+public double preciseWidth_;
+/**
+ * The height in double precision.
+ */
+public double preciseHeight_;
+
+/**
+ * Constructs a new precision dimension.
+ */
+public this() {
+}
+
+/**
+ * Constructs a new precision dimension with the given values.
+ * @param width the width
+ * @param height the height
+ */
+public this(double width, double height) {
+    preciseWidth_ = width;
+    preciseHeight_ = height;
+    updateInts();
+}
+
+/**
+ * Constructs a precision representation of the given dimension.
+ * @param d the reference dimension
+ */
+public this(Dimension d) {
+    preciseHeight_ = d.preciseHeight();
+    preciseWidth_ = d.preciseWidth();
+    updateInts();
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Dimension#performScale(double)
+ */
+public void performScale(double factor) {
+    preciseHeight_ *= factor;
+    preciseWidth_ *= factor;
+    updateInts();
+}
+
+/**
+ * Updates the integer fields using the precise versions.
+ */
+public final void updateInts() {
+    width = cast(int)Math.floor(preciseWidth_ + 0.000000001);
+    height = cast(int)Math.floor(preciseHeight_ + 0.000000001);
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Dimension#preciseWidth()
+ */
+public double preciseWidth() {
+    return preciseWidth_;
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Dimension#preciseHeight()
+ */
+public double preciseHeight() {
+    return preciseHeight_;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/geometry/PrecisionPoint.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.geometry.PrecisionPoint;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.geometry.Point;
+
+/**
+ * @author danlee
+ */
+public class PrecisionPoint : Point {
+
+/** Double value for X **/
+public double preciseX_;
+
+/** Double value for Y **/
+public double preciseY_;
+
+/**
+ * Constructor for PrecisionPoint.
+ */
+public this() {
+    super();
+}
+
+/**
+ * Constructor for PrecisionPoint.
+ * @param copy Point from which the initial values are taken
+ */
+public this(Point copy) {
+    preciseX_ = copy.preciseX();
+    preciseY_ = copy.preciseY();
+    updateInts();
+}
+
+/**
+ * Constructor for PrecisionPoint.
+ * @param x X value
+ * @param y Y value
+ */
+public this(int x, int y) {
+    super(x, y);
+    preciseX_ = x;
+    preciseY_ = y;
+}
+
+/**
+ * Constructor for PrecisionPoint.
+ * @param x X value
+ * @param y Y value
+ */
+public this(double x, double y) {
+    preciseX_ = x;
+    preciseY_ = y;
+    updateInts();
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Point#getCopy()
+ */
+public Point getCopy() {
+    return new PrecisionPoint(preciseX_, preciseY_);
+}
+
+
+/**
+ * @see dwtx.draw2d.geometry.Point#performScale(double)
+ */
+public void performScale(double factor) {
+    preciseX_ = preciseX_ * factor;
+    preciseY_ = preciseY_ * factor;
+    updateInts();
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Point#performTranslate(int, int)
+ */
+public void performTranslate(int dx, int dy) {
+    preciseX_ += dx;
+    preciseY_ += dy;
+    updateInts();
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Point#setLocation(Point)
+ */
+public Point setLocation(Point pt) {
+    preciseX_ = pt.preciseX();
+    preciseY_ = pt.preciseY();
+    updateInts();
+    return this;
+}
+
+/**
+ * Updates the integer fields using the precise versions.
+ */
+public final void updateInts() {
+    x = cast(int)Math.floor(preciseX_ + 0.000000001);
+    y = cast(int)Math.floor(preciseY_ + 0.000000001);
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Point#preciseX()
+ */
+public double preciseX() {
+    return preciseX_;
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Point#preciseY()
+ */
+public double preciseY() {
+    return preciseY_;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/geometry/PrecisionRectangle.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,396 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.geometry.PrecisionRectangle;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.PrecisionPoint;
+
+/**
+ * A Rectangle implementation using floating point values which are truncated into the inherited
+ * integer fields. The use of floating point prevents rounding errors from accumulating.
+ * @author hudsonr
+ * Created on Apr 9, 2003
+ */
+public final class PrecisionRectangle : Rectangle {
+
+/** Double value for height */
+public double preciseHeight_;
+
+/** Double value for width */
+public double preciseWidth_;
+
+/** Double value for X */
+public double preciseX_;
+
+/** Double value for Y */
+public double preciseY_;
+
+/**
+ * Constructs a new PrecisionRectangle with all values 0.
+ */
+public this() { }
+
+/**
+ * Constructs a new PrecisionRectangle from the given integer Rectangle.
+ * @param rect the base rectangle
+ */
+public this(Rectangle rect) {
+    preciseX_ = rect.preciseX();
+    preciseY_ = rect.preciseY();
+    preciseWidth_ = rect.preciseWidth();
+    preciseHeight_ = rect.preciseHeight();
+    updateInts();
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Rectangle#getCopy()
+ */
+public Rectangle getCopy() {
+    return getPreciseCopy();
+}
+
+/**
+ * Returns a precise copy of this.
+ * @return a precise copy
+ */
+public PrecisionRectangle getPreciseCopy() {
+    PrecisionRectangle result = new PrecisionRectangle();
+    result.preciseX_ = preciseX_;
+    result.preciseY_ = preciseY_;
+    result.preciseWidth_ = preciseWidth_;
+    result.preciseHeight_ = preciseHeight_;
+    result.updateInts();
+    return result;
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Rectangle#crop(dwtx.draw2d.geometry.Insets)
+ */
+public Rectangle crop(Insets insets) {
+    if (insets is null)
+        return this;
+    setX(preciseX_ + insets.left);
+    setY(preciseY_ + insets.top);
+    setWidth(preciseWidth_ - (insets.getWidth()));
+    setHeight(preciseHeight_ - (insets.getHeight()));
+
+    return this;
+}
+
+/**
+ * @see Rectangle#equals(Object)
+ */
+public override int opEquals(Object o) {
+    if ( auto pr = cast(PrecisionRectangle)o ) {
+        return super.opEquals(o)
+            && Math.abs(pr.preciseX_ - preciseX_) < 0.000000001
+            && Math.abs(pr.preciseY_ - preciseY_) < 0.000000001
+            && Math.abs(pr.preciseWidth_ - preciseWidth_) < 0.000000001
+            && Math.abs(pr.preciseHeight_ - preciseHeight_) < 0.00000001;
+    }
+
+    return super.opEquals(o);
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Rectangle#performScale(double)
+ */
+public void performScale(double factor) {
+    preciseX_ *= factor;
+    preciseY_ *= factor;
+    preciseWidth_ *= factor;
+    preciseHeight_ *= factor;
+    updateInts();
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Rectangle#performTranslate(int, int)
+ */
+public void performTranslate(int dx, int dy) {
+    preciseX_ += dx;
+    preciseY_ += dy;
+    x += dx;
+    y += dy;
+}
+
+/**
+ * Returns the bottom coordinte in double precision.
+ * @return the precise bottom
+ */
+public double preciseBottom() {
+    return preciseHeight_ + preciseY_;
+}
+
+/**
+ * Returns the right side in double precision.
+ * @return the precise right
+ */
+public double preciseRight() {
+    return preciseWidth_ + preciseX_;
+}
+
+
+/**
+ * @see dwtx.draw2d.geometry.Rectangle#resize(dwtx.draw2d.geometry.Dimension)
+ */
+public Rectangle resize(Dimension sizeDelta) {
+    preciseWidth_ += sizeDelta.preciseWidth();
+    preciseHeight_ += sizeDelta.preciseHeight();
+    updateInts();
+    return this;
+}
+
+/**
+ * Sets the height.
+ * @param value the new height
+ */
+public void setHeight(double value) {
+    preciseHeight_ = value;
+    height = cast(int)Math.floor(preciseHeight_ + 0.000000001);
+}
+
+/**
+ * Sets the width.
+ * @param value the new width
+ */
+public void setWidth(double value) {
+    preciseWidth_ = value;
+    width = cast(int)Math.floor(preciseWidth_ + 0.000000001);
+}
+
+/**
+ * Sets the x value.
+ * @param value the new x value
+ */
+public void setX(double value) {
+    preciseX_ = value;
+    x = cast(int)Math.floor(preciseX_ + 0.000000001);
+}
+
+/**
+ * Sets the y value.
+ * @param value the new y value
+ */
+public void setY(double value) {
+    preciseY_ = value;
+    y = cast(int)Math.floor(preciseY_ + 0.000000001);
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Rectangle#translate(dwtx.draw2d.geometry.Point)
+ */
+public Rectangle translate(Point p) {
+    preciseX_ += p.preciseX();
+    preciseY_ += p.preciseY();
+    updateInts();
+    return this;
+}
+
+/**
+ * Unions the given PrecisionRectangle with this rectangle and returns <code>this</code>
+ * for convenience.
+ * @since 3.0
+ * @param other the rectangle being unioned
+ * @return <code>this</code> for convenience
+ * @deprecated
+ * Use {@link #union(Rectangle)} instead
+ */
+public PrecisionRectangle union_(PrecisionRectangle other) {
+    double newright = Math.max(preciseRight(), other.preciseRight());
+    double newbottom = Math.max(preciseBottom(), other.preciseBottom());
+    preciseX_ = Math.min(preciseX_, other.preciseX_);
+    preciseY_ = Math.min(preciseY_, other.preciseY_);
+    preciseWidth_ = newright - preciseX_;
+    preciseHeight_ = newbottom - preciseY_;
+    updateInts();
+
+    return this;
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Rectangle#union(dwtx.draw2d.geometry.Rectangle)
+ */
+public Rectangle union_(Rectangle other) {
+    double newright = Math.max(preciseRight(), other.preciseX() + other.preciseWidth());
+    double newbottom = Math.max(preciseBottom(), other.preciseY() + other.preciseHeight());
+    preciseX_ = Math.min(preciseX_, other.preciseX());
+    preciseY_ = Math.min(preciseY_, other.preciseY());
+    preciseWidth_ = newright - preciseX_;
+    preciseHeight_ = newbottom - preciseY_;
+    updateInts();
+
+    return this;
+}
+
+/**
+ * Updates the integer values based on the current precise values.  The integer values ar
+ * the floor of the double values.  This is called automatically when calling api which is
+ * overridden in this class.
+ * @since 3.0
+ */
+public void updateInts() {
+    x = cast(int)Math.floor(preciseX_ + 0.000000001);
+    y = cast(int)Math.floor(preciseY_ + 0.000000001);
+    width = cast(int)Math.floor(preciseWidth_ + preciseX_ + 0.000000001) - x;
+    height = cast(int)Math.floor(preciseHeight_ + preciseY_ + 0.000000001) - y;
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Rectangle#union(dwtx.draw2d.geometry.Point)
+ */
+public void union_(Point p) {
+    if (p.preciseX() < preciseX_) {
+        preciseWidth_ += (preciseX_ - p.preciseX());
+        preciseX_ = p.preciseX();
+    } else {
+        double right = preciseX_ + preciseWidth_;
+        if (p.preciseX() > right) {
+            preciseWidth_ = p.preciseX() - preciseX_;
+        }
+    }
+    if (p.preciseY() < preciseY_) {
+        preciseHeight_ += (preciseY - p.preciseY());
+        preciseY_ = p.preciseY();
+    } else {
+        double bottom = preciseY_ + preciseHeight_;
+        if (p.preciseY() > bottom) {
+            preciseHeight_ = p.preciseY() - preciseY_;
+        }
+    }
+    updateInts();
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Rectangle#transpose()
+ */
+public Rectangle transpose() {
+    double temp = preciseX_;
+    preciseX_ = preciseY_;
+    preciseY_ = temp;
+    temp = preciseWidth_;
+    preciseWidth_ = preciseHeight_;
+    preciseHeight_ = temp;
+    super.transpose();
+    return this;
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Rectangle#setLocation(dwtx.draw2d.geometry.Point)
+ */
+public Rectangle setLocation(Point loc) {
+    preciseX_ = loc.preciseX();
+    preciseY_ = loc.preciseY();
+    updateInts();
+    return this;
+}
+
+/**
+ * Returns the precise geometric centre of the rectangle
+ *
+ * @return <code>PrecisionPoint</code> geometric center of the rectangle
+ * @since 3.4
+ */
+public Point getCenter() {
+    return new PrecisionPoint(preciseX_ + preciseWidth_ / 2.0, preciseY_ + preciseHeight_ / 2.0);
+}
+
+/**
+ * Shrinks the sides of this Rectangle by the horizontal and vertical values
+ * provided as input, and returns this Rectangle for convenience. The center of
+ * this Rectangle is kept constant.
+ *
+ * @param h  Horizontal reduction amount
+ * @param v  Vertical reduction amount
+ * @return  <code>this</code> for convenience
+ * @since 3.4
+ */
+public Rectangle shrink(double h, double v) {
+    preciseX_ += h;
+    preciseWidth_ -= (h + h);
+    preciseY_ += v;
+    preciseHeight_ -= (v + v);
+    updateInts();
+    return this;
+}
+
+/**
+ * Expands the horizontal and vertical sides of this Rectangle with the values
+ * provided as input, and returns this for convenience. The location of its
+ * center is kept constant.
+ *
+ * @param h  Horizontal increment
+ * @param v  Vertical increment
+ * @return  <code>this</code> for convenience
+ * @since 3.4
+ */
+public Rectangle expand(double h, double v) {
+    return shrink(-h, -v);
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Rectangle#shrink(int, int)
+ */
+public Rectangle shrink(int h, int v) {
+    return shrink(cast(double)h, cast(double)v);
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Rectangle#contains(dwtx.draw2d.geometry.Point)
+ */
+public bool contains(Point p) {
+    return preciseX_ <= p.preciseX() && p.preciseX() <= preciseX_ + preciseWidth_
+    && preciseY_ <= p.preciseY() && p.preciseY() <= preciseY_ + preciseHeight_;
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Rectangle#preciseX()
+ */
+public double preciseX() {
+    return preciseX_;
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Rectangle#preciseY()
+ */
+public double preciseY() {
+    return preciseY_;
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Rectangle#preciseWidth()
+ */
+public double preciseWidth() {
+    return preciseWidth_;
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Rectangle#preciseHeight()
+ */
+public double preciseHeight() {
+    return preciseHeight_;
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Rectangle#setSize(dwtx.draw2d.geometry.Dimension)
+ */
+public Rectangle setSize(Dimension d) {
+    preciseWidth_ = d.preciseWidth();
+    preciseHeight_ = d.preciseHeight();
+    return super.setSize(d);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/geometry/Ray.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.geometry.Ray;
+
+import dwt.dwthelper.utils;
+import tango.text.convert.Format;
+
+import dwtx.draw2d.geometry.Point;
+
+
+/**
+ * Represents a 2-dimensional directional Vector, or Ray. {@link java.util.Vector} is
+ * commonly imported, so the name Ray was chosen.
+ */
+public final class Ray {
+
+/** the X value */
+public int x;
+/** the Y value*/
+public int y;
+
+/**
+ * Constructs a Ray &lt;0, 0&gt; with no direction and magnitude.
+ * @since 2.0
+ */
+public this() { }
+
+/**
+ * Constructs a Ray pointed in the specified direction.
+ *
+ * @param x  X value.
+ * @param y  Y value.
+ * @since 2.0
+ */
+public this(int x, int y) {
+    this.x = x;
+    this.y = y;
+}
+
+/**
+ * Constructs a Ray pointed in the direction specified by a Point.
+ * @param p the Point
+ * @since 2.0
+ */
+public this(Point p) {
+    x = p.x; y = p.y;
+}
+
+/**
+ * Constructs a Ray representing the direction and magnitude between to provided Points.
+ * @param start Strarting Point
+ * @param end End Point
+ * @since 2.0
+ */
+public this(Point start, Point end) {
+    x = end.x - start.x;
+    y = end.y - start.y;
+}
+
+/**
+ * Constructs a Ray representing the difference between two provided Rays.
+ * @param start  The start Ray
+ * @param end   The end Ray
+ * @since 2.0
+ */
+public this(Ray start, Ray end) {
+    x = end.x - start.x;
+    y = end.y - start.y;
+}
+
+/**
+ * Calculates the magnitude of the cross product of this Ray with another.
+ * Represents the amount by which two Rays are directionally different.
+ * Parallel Rays return a value of 0.
+ * @param r  Ray being compared
+ * @return  The assimilarity
+ * @see #similarity(Ray)
+ * @since 2.0
+ */
+public int assimilarity(Ray r) {
+    return Math.abs(x * r.y - y * r.x);
+}
+
+/**
+ * Calculates the dot product of this Ray with another.
+ * @param r the Ray used to perform the dot product
+ * @return The dot product
+ * @since 2.0
+ */
+public int dotProduct(Ray r) {
+    return x * r.x + y * r.y;
+}
+
+/**
+ * @see java.lang.Object#equals(Object)
+ */
+public override int opEquals(Object obj) {
+    if (obj is this)
+        return true;
+    if ( auto r = cast(Ray)obj ) {
+        return x is r.x && y is r.y;
+    }
+    return false;
+}
+
+/**
+ * Creates a new Ray which is the sum of this Ray with another.
+ * @param r  Ray to be added with this Ray
+ * @return  a new Ray
+ * @since 2.0
+ */
+public Ray getAdded(Ray r) {
+    return new Ray(r.x + x, r.y + y);
+}
+
+/**
+ * Creates a new Ray which represents the average of this Ray with another.
+ * @param r  Ray to calculate the average.
+ * @return  a new Ray
+ * @since 2.0
+ */
+public Ray getAveraged(Ray r) {
+    return new Ray ((x + r.x) / 2, (y + r.y) / 2);
+}
+
+/**
+ * Creates a new Ray which represents this Ray scaled by the amount provided.
+ * @param s  Value providing the amount to scale.
+ * @return  a new Ray
+ * @since 2.0
+ */
+public Ray getScaled(int s) {
+    return new Ray(x * s, y * s);
+}
+
+/**
+ * @see java.lang.Object#toHash()
+ */
+public override hash_t toHash() {
+    return (x * y) ^ (x + y);
+}
+
+/**
+ * Returns true if this Ray has a non-zero horizontal comonent.
+ * @return  true if this Ray has a non-zero horizontal comonent
+ * @since 2.0
+ */
+public bool isHorizontal() {
+    return x !is 0;
+}
+
+/**
+ * Returns the length of this Ray.
+ * @return  Length of this Ray
+ * @since 2.0
+ */
+public double length() {
+    return Math.sqrt(cast(real)dotProduct(this));
+}
+
+/**
+ * Calculates the similarity of this Ray with another.
+ * Similarity is defined as the absolute value of the dotProduct()
+ * @param r  Ray being tested for similarity
+ * @return  the Similarity
+ * @see #assimilarity(Ray)
+ * @since 2.0
+ */
+public int similarity(Ray r) {
+    return Math.abs(dotProduct(r));
+}
+
+/**
+ * @return a String representation
+ */
+public String toString() {
+    return Format("({}, {})", x, y );//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/geometry/Rectangle.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,977 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.geometry.Rectangle;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Translatable;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.PositionConstants;
+import tango.text.convert.Format;
+
+static import dwt.graphics.Rectangle;
+
+/**
+ * Represents a Rectangle(x, y, width, height). This class provides various methods
+ * for manipulating this Rectangle or creating new derived geometrical Objects.
+ */
+public class Rectangle
+    : Cloneable, /+java.io.Serializable, +/Translatable
+{
+/** the X value */
+public int x;
+/** the Y value */
+public int y;
+/** the width*/
+public int width;
+/** the height */
+public int height;
+
+/**A singleton for use in short calculations.  Use to avoid newing unnecessary objects.*/
+public static const Rectangle SINGLETON;
+
+static this(){
+    SINGLETON = new Rectangle();
+}
+
+static final long serialVersionUID = 1;
+
+/**
+ * Constructs a Rectangle at the origin with zero width and height.
+ *
+ * @since 2.0
+ */
+public this() { }
+
+/**
+ * Constructs a Rectangle given a location and size.
+ *
+ * @param p  the location
+ * @param size the size
+ * @since 2.0
+ */
+public this(Point p, Dimension size) {
+    this(p.x, p.y, size.width, size.height);
+}
+
+/**
+ * Constructs a copy of the provided Rectangle.
+ *
+ * @param rect Rectangle supplying the initial values
+ * @since 2.0
+ */
+public this(Rectangle rect) {
+    this(rect.x, rect.y, rect.width, rect.height);
+}
+
+/**
+ * Constructs a copy of the provided DWT {@link dwt.graphics.Rectangle}.
+ *
+ * @param rect The DWT Rectangle being copied
+ * @since 2.0
+ */
+public this(dwt.graphics.Rectangle.Rectangle rect) {
+    this(rect.x, rect.y, rect.width, rect.height);
+}
+
+/**
+ * Constructs a Rectangle with the provided values.
+ *
+ * @param x  X location
+ * @param y  Y location
+ * @param width  Width of the rectangle
+ * @param height Height of the rectangle
+ * @since 2.0
+ */
+public this(int x, int y, int width, int height) {
+    this.x = x;
+    this.y = y;
+    this.width = width;
+    this.height = height;
+}
+
+/**
+ * Constructs the smallest Rectangle that contains the specified Points.
+ *
+ * @param p1  Upper left hand corner
+ * @param p2  Lower right hand corner
+ * @since 2.0
+ */
+public this(Point p1, Point p2) {
+    this.x = Math.min(p1.x, p2.x);
+    this.y = Math.min(p1.y, p2.y);
+    this.width = Math.abs(p2.x - p1.x) + 1;
+    this.height = Math.abs(p2.y - p1.y) + 1;
+}
+
+/**
+ * Returns the y-coordinate of the bottom of this Rectangle.
+ *
+ * @return  The Y coordinate of the bottom
+ * @since 2.0
+ */
+public int bottom() {
+    return y + height;
+}
+
+/**
+ * Returns whether the given point is within the boundaries of this Rectangle.
+ * The boundaries are inclusive of the top and left edges, but exclusive of the
+ * bottom and right edges.
+ *
+ * @param pt  Point being tested for containment
+ * @return true if the Point is within this Rectangle
+ * @since 2.0
+ */
+public bool contains(Point pt) {
+    return contains(pt.x, pt.y);
+}
+
+/**
+ * Returns <code>true</code> if the given rectangle is contained within the
+ * boundaries of this Rectangle.
+ *
+ * @param rect the Rectangle to test
+ * @return true if the Rectangle is within this Rectangle
+ */
+public bool contains(Rectangle rect) {
+    return x <= rect.x
+      && y <= rect.y
+      && right() >= rect.right()
+      && bottom() >= rect.bottom();
+}
+
+/**
+ * Returns whether the given coordinates are within the boundaries of this
+ * Rectangle.  The boundaries are inclusive of the top and left edges, but
+ * exclusive of the bottom and right edges.
+ *
+ * @param x X value
+ * @param y Y value
+ * @return true if the coordinates are within this Rectangle
+ * @since 2.0
+ */
+public bool contains(int x, int y) {
+    return y >= this.y
+        && y < this.y + this.height
+        && x >= this.x
+        && x < this.x + this.width;
+}
+
+/**
+ * Crops this rectangle by the amount specified in <code>insets</code>.
+ *
+ * @param insets  Insets to be removed from the Rectangle
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Rectangle crop(Insets insets) {
+    if (insets is null)
+        return this;
+    x += insets.left;
+    y += insets.top;
+    width -= (insets.getWidth());
+    height -= (insets.getHeight());
+    return this;
+}
+
+/**
+ * Returns whether the input object is equal to this Rectangle or not.
+ * Rectangles are equivalent if their x, y, height, and width values are the
+ * same.
+ *
+ * @param o  Object being tested for equality
+ * @return  Returns the result of the equality test
+ * @since 2.0
+ */
+public override int opEquals(Object o) {
+    if (this is o) return true;
+    if (auto r = cast(Rectangle)o ) {
+        return (x is r.x)
+            && (y  is r.y)
+            && (width  is r.width)
+            && (height is r.height);
+    }
+    return false;
+}
+
+/**
+ * Expands the horizontal and vertical sides of this Rectangle with the values
+ * provided as input, and returns this for convenience. The location of its
+ * center is kept constant.
+ *
+ * @param h  Horizontal increment
+ * @param v  Vertical increment
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Rectangle expand(int h, int v) {
+    return shrink(-h, -v);
+}
+
+/**
+ * Expands the horizontal and vertical sides of this Rectangle by the width and
+ * height of the given Insets, and returns this for convenience.
+ *
+ * @param insets contains the amounts to expand on each side
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Rectangle expand(Insets insets) {
+    x -= insets.left;
+    y -= insets.top;
+    height += insets.getHeight();
+    width  += insets.getWidth();
+    return this;
+}
+
+/**
+ * Returns a new Point representing the middle point of the bottom side of this
+ * Rectangle.
+ *
+ * @return  Point at the bottom of the Rectangle
+ * @since 2.0
+ */
+public Point getBottom() {
+    return new Point(x + width / 2, bottom());
+}
+
+/**
+ * Returns a new Point representing the bottom left point of this Rectangle.
+ *
+ * @return  Point at the bottom left of the rectangle
+ * @since 2.0
+ */
+public Point getBottomLeft() {
+    return new Point(x, y + height);
+}
+
+/**
+ * Returns a new Point representing the bottom right point of this Rectangle.
+ *
+ * @return  Point at the bottom right of the rectangle
+ * @since 2.0
+ */
+public Point getBottomRight() {
+    return new Point(x + width, y + height);
+}
+
+/**
+ * Returns a new point representing the center of this Rectangle.
+ *
+ * @return  Point at the center of the rectangle
+ */
+public Point getCenter() {
+    return new Point(x + width / 2, y + height / 2);
+}
+
+/**
+ * Returns a new Rectangle which has the exact same parameters as this
+ * Rectangle.
+ *
+ * @return  Copy of this Rectangle
+ * @since 2.0
+ */
+public Rectangle getCopy() {
+//     try {
+//         return cast(Rectangle)clone();
+//     } catch (CloneNotSupportedException exc) {
+        return new Rectangle(this);
+//     }
+}
+
+/**
+ * Returns a new Rectangle with the specified insets cropped.
+ *
+ * @param insets  Insets being cropped from the Rectangle
+ * @return  Cropped new Rectangle
+ */
+public Rectangle getCropped(Insets insets) {
+    Rectangle r = new Rectangle(this);
+    r.crop(insets);
+    return r;
+}
+
+/**
+ * Returns a new incremented Rectangle, where the sides are expanded by the
+ * horizonatal and vertical values provided. The center of the Rectangle is
+ * maintained constant.
+ *
+ * @param h  Horizontal increment
+ * @param v  Vertical inrement
+ * @return  A new expanded Rectangle
+ * @since 2.0
+ */
+public Rectangle getExpanded(int h, int v) {
+    return (new Rectangle(this)).expand(h, v);
+}
+
+/**
+ * Creates and returns a new Rectangle with the bounds of <code>this</code>
+ * Rectangle, expanded by the given Insets.
+ *
+ * @param insets The insets used to expand this rectangle
+ * @return A new expanded Rectangle
+ * @since 2.0
+ */
+public Rectangle getExpanded(Insets insets) {
+    return (new Rectangle(this)).expand(insets);
+}
+
+
+/**
+ * Returns a new Rectangle which has the intersection of this Rectangle and the
+ * rectangle provided as input. Returns an empty Rectangle if there is no
+ * interection.
+ *
+ * @param rect  Rectangle provided to test for intersection
+ * @return  A new Rectangle representing the intersection
+ * @since 2.0
+ */
+public Rectangle getIntersection(Rectangle rect) {
+    int x1 = Math.max(x, rect.x);
+    int x2 = Math.min(x + width, rect.x + rect.width);
+    int y1 = Math.max(y, rect.y);
+    int y2 = Math.min(y + height, rect.y + rect.height);
+    if (((x2 - x1) < 0) || ((y2 - y1) < 0))
+        return new Rectangle(0, 0, 0, 0);  // No intersection
+    else
+        return new Rectangle(x1, y1, x2 - x1, y2 - y1);
+}
+
+/**
+ * Returns a new Point representing the middle point of the left hand side of
+ * this Rectangle.
+ *
+ * @return  Point at the left of the Rectangle
+ */
+public Point getLeft() {
+    return new Point(x, y + height / 2);
+}
+
+/**
+ * Returns the upper left hand corner of the rectangle.
+ *
+ * @return  Location of the rectangle
+ * @see  #setLocation(Point)
+ */
+public Point getLocation() {
+    return new Point(x, y);
+}
+
+/**
+ * <P>Returns an integer which represents the position of the given point with  respect to
+ * this rectangle. Possible return values are bitwise ORs of the constants WEST, EAST,
+ * NORTH, and SOUTH as found in {@link dwtx.draw2d.PositionConstants}.
+ *
+ * <P>Returns PositionConstant.NONE if the given point is inside this Rectangle.
+ *
+ * @param   pt  The Point whose position has to be determined
+ * @return  An <code>int</code> which is a PositionConstant
+ * @see dwtx.draw2d.PositionConstants
+ * @since 2.0
+ */
+public int getPosition(Point pt) {
+    int result = PositionConstants.NONE;
+
+    if (contains(pt))
+        return result;
+
+    if (pt.x < x)
+        result = PositionConstants.WEST;
+    else if (pt.x >= (x + width))
+        result = PositionConstants.EAST;
+
+    if (pt.y < y)
+        result = result | PositionConstants.NORTH;
+    else if (pt.y >= (y + height))
+        result = result | PositionConstants.SOUTH;
+
+    return result;
+}
+
+/**
+ * Returns a new Rectangle which is equivalent to this Rectangle with its
+ * dimensions modified by the passed width <i>w</i> and height <i>h</i>.
+
+ * @param w  Amount by which width is to be resized
+ * @param h  Amount by which height is to be resized
+ * @return a new rectangle with its width and height modified
+ */
+public Rectangle getResized(int w, int h) {
+    return (new Rectangle(this)).resize(w, h);
+}
+
+/**
+ * Returns a new Rectangle which is equivalent to this Rectangle with its
+ * dimensions modified by the passed Dimension <i>d</i>.
+ *
+ * @param d  Dimensions by which the rectangle's size should be modified
+ * @return   The new rectangle with the modified dimensions
+ * @since 2.0
+ */
+public Rectangle getResized(Dimension d) {
+    return (new Rectangle(this)).resize(d);
+}
+
+/**
+ * Returns a new Point which represents the middle point of the right hand side
+ * of this Rectangle.
+ *
+ * @return  Point at the right of the Rectangle
+ * @since 2.0
+ */
+public Point getRight() {
+    return new Point(right(), y + height / 2);
+}
+
+/**
+ * Retuns the dimensions of this Rectangle.
+ *
+ * @return  Size of this Rectangle as a Dimension
+ * @since 2.0
+ */
+public Dimension getSize() {
+    return new Dimension(width, height);
+}
+
+/**
+ * Returns a new Point which represents the middle point of the top side of this
+ * Rectangle.
+ *
+ * @return  Point at the top of the Rectangle
+ * @since 2.0
+ */
+public Point getTop() {
+    return new Point(x + width / 2, y);
+}
+
+/**
+ * Returns a new Point which represents the top left hand corner of this
+ * Rectangle.
+ *
+ * @return  Point at the top left of the rectangle
+ * @since 2.0
+ */
+public Point getTopLeft() {
+    return new Point(x, y);
+}
+
+/**
+ * Returns a new Point which represents the top right hand corner of this
+ * Rectangle.
+ *
+ * @return  Point at the top right of the rectangle
+ * @since 2.0
+ */
+public Point getTopRight() {
+    return new Point(x + width, y);
+}
+
+/**
+ * Returns a new Rectangle which is shifted along each axis by the passed
+ * values.
+ *
+ * @param dx  Displacement along X axis
+ * @param dy  Displacement along Y axis
+ * @return  The new translated rectangle
+ * @since 2.0
+ */
+public Rectangle getTranslated(int dx, int dy) {
+    return (new Rectangle(this)).translate(dx, dy);
+}
+
+/**
+ * Returns a new Rectangle which is shifted by the position of the given Point.
+ *
+ * @param pt  Point providing the amount of shift along each axis
+ * @return  The new translated Rectangle
+ * @since 2.0
+ */
+public Rectangle getTranslated(Point pt) {
+    return (new Rectangle(this)).translate(pt);
+}
+
+/**
+ * Returns a new rectangle whose width and height have been interchanged, as well
+ * as its x and y values. This can be useful in orientation changes.
+ *
+ * @return  The transposed rectangle
+ * @since 2.0
+ */
+public Rectangle getTransposed() {
+    Rectangle r = new Rectangle(this);
+    r.transpose();
+    return r;
+}
+
+/**
+ * Returns a new Rectangle which contains both this Rectangle and the Rectangle
+ * supplied as input.
+ *
+ * @param rect  Rectangle for calculating union_
+ * @return  A new unioned Rectangle
+ * @since 2.0
+ */
+public Rectangle getUnion(Rectangle rect) {
+    if (rect is null || rect.isEmpty())
+        return new Rectangle(this);
+    Rectangle union_ = new Rectangle(
+        Math.min(x, rect.x),
+        Math.min(y, rect.y), 0, 0);
+    union_.width = Math.max(x + width, rect.x + rect.width) - union_.x;
+    union_.height = Math.max(y + height, rect.y + rect.height) - union_.y;
+    return union_;
+}
+
+/**
+ * @see java.lang.Object#toHash()
+ */
+public override hash_t toHash() {
+    return (x + height + 1) * (y + width + 1) ^ x ^ y;
+}
+
+/**
+ * Sets the size of this Rectangle to the intersection region with the Rectangle
+ * supplied as input, and returns this for convenience. The location and
+ * dimensions are set to zero if there is no intersection with the input
+ * Rectangle.
+ *
+ * @param rect  Rectangle for the calculating intersection.
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Rectangle intersect(Rectangle rect) {
+    int x1 = Math.max(x, rect.x);
+    int x2 = Math.min(x + width, rect.x + rect.width);
+    int y1 = Math.max(y, rect.y);
+    int y2 = Math.min(y + height, rect.y + rect.height);
+    if (((x2 - x1) < 0) || ((y2 - y1) < 0))
+        x = y = width = height = 0; // No intersection
+    else {
+        x = x1;
+        y = y1;
+        width = x2 - x1;
+        height = y2 - y1;
+    }
+    return this;
+}
+
+/**
+ * Returns <code>true</code> if the input Rectangle intersects this Rectangle.
+ *
+ * @param rect  Rectangle for the intersetion test
+ * @return  <code>true</code> if the input Rectangle intersects this Rectangle
+ * @since 2.0
+ */
+public bool intersects(Rectangle rect) {
+    return rect.x < x + width
+        && rect.y < y + height
+        && rect.x + rect.width > x
+        && rect.y + rect.height > y;
+}
+
+/**
+ * Returns <code>true</code> if this Rectangle's width or height is less than or
+ * equal to 0.
+ *
+ * @return  <code>true</code> if this Rectangle is empty
+ * @since 2.0
+ */
+public bool isEmpty() {
+    return width <= 0 || height <= 0;
+}
+
+/**
+ * @see Translatable#performScale(double)
+ */
+public void performScale(double factor) {
+    scale(factor);
+}
+
+/**
+ * @see Translatable#performTranslate(int, int)
+ */
+public void performTranslate(int dx, int dy) {
+    translate(dx, dy);
+}
+
+/**
+ * Resizes this Rectangle by the Dimension provided as input and returns this
+ * for convenience. This Rectange's width will become this.width + sizeDelta.width.
+ * Likewise for height.
+ *
+ * @param sizeDelta  Resize data as a Dimension
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Rectangle resize(Dimension sizeDelta) {
+    width += sizeDelta.width;
+    height += sizeDelta.height;
+    return this;
+}
+
+/**
+ * Resizes this Rectangle by the values supplied as input and returns this for
+ * convenience. This Rectangle's width will become this.width + dw. This
+ * Rectangle's height will become this.height + dh.
+ *
+ * @param dw  Amount by which width is to be resized
+ * @param dh  Amount by which height is to be resized
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Rectangle resize(int dw, int dh) {
+    width += dw;
+    height += dh;
+    return this;
+}
+
+/**
+ * Returns the x-coordinate of the right side of this Rectangle.
+ *
+ * @return  The X coordinate of the right side
+ * @since 2.0
+ */
+public int right() {
+    return x + width;
+}
+
+/**
+ * Scales the location and size of this Rectangle by the given scale and returns
+ * this for convenience.
+ *
+ * @param   scaleFactor The factor by which this rectangle will be scaled
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public final Rectangle scale(double scaleFactor) {
+    return scale(scaleFactor, scaleFactor);
+}
+
+/**
+ * Scales the location and size of this Rectangle by the given scales and returns
+ * this for convenience.
+ *
+ * @param   scaleX  the factor by which the X dimension has to be scaled
+ * @param   scaleY  the factor by which the Y dimension has to be scaled
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Rectangle scale(double scaleX, double scaleY) {
+    int oldX = x;
+    int oldY = y;
+    x = cast(int)(Math.floor(x * scaleX));
+    y = cast(int)(Math.floor(y * scaleY));
+    width = cast(int)(Math.ceil((oldX + width) * scaleX)) - x;
+    height = cast(int)(Math.ceil((oldY + height) * scaleY)) - y;
+    return this;
+}
+
+/**
+ * Sets the parameters of this Rectangle from the Rectangle passed in and
+ * returns this for convenience.
+ *
+ * @return  <code>this</code> for convenience
+ * @param rect Rectangle providing the bounding values
+ * @since 2.0
+ */
+public Rectangle setBounds(Rectangle rect) {
+    x = rect.x;
+    y = rect.y;
+    width = rect.width;
+    height = rect.height;
+    return this;
+}
+
+/**
+ * Sets the location of this Rectangle to the point given as input and returns
+ * this for convenience.
+ *
+ * @return  <code>this</code> for convenience
+ * @param p  New position of this Rectangle
+ * @since 2.0
+ */
+public Rectangle setLocation(Point p) {
+    x = p.x;
+    y = p.y;
+    return this;
+}
+
+/**
+ * Sets the location of this Rectangle to the coordinates given as input and
+ * returns this for convenience.
+ *
+ * @param x1  The new X coordinate
+ * @param y1  The new Y coordinate
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Rectangle setLocation(int x1, int y1) {
+    x = x1;
+    y = y1;
+    return this;
+}
+
+/**
+ * Sets the width and height of this Rectangle to the width and height of the
+ * given Dimension and returns this for convenience.
+ *
+ * @param d  The new Dimension
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Rectangle setSize(Dimension d) {
+    width = d.width;
+    height = d.height;
+    return this;
+}
+
+/**
+ * Sets the width of this Rectangle to <i>w</i> and the height of this
+ * Rectangle to <i>h</i> and returns this for convenience.
+ *
+ * @return  <code>this</code> for convenience
+ * @param w  The new width
+ * @param h  The new height
+ * @since 2.0
+ */
+public Rectangle setSize(int w, int h) {
+    width = w;
+    height = h;
+    return this;
+}
+
+/**
+ * Shrinks the sides of this Rectangle by the horizontal and vertical values
+ * provided as input, and returns this Rectangle for convenience. The center of
+ * this Rectangle is kept constant.
+ *
+ * @param h  Horizontal reduction amount
+ * @param v  Vertical reduction amount
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Rectangle shrink(int h, int v) {
+    x += h;
+    width -= (h + h);
+    y += v;
+    height -= (v + v);
+    return this;
+}
+
+/**
+ * Returns the description of this Rectangle.
+ *
+ * @return String containing the description
+ * @since 2.0
+ */
+public override String toString() {
+    return Format("Rectangle({}, {}, {}, {})", x, y, width, height );
+}
+
+/**
+ * Returns <code>true</code> if the input Rectangle touches this Rectangle.
+ *
+ * @param rect  Rectangle being checked for contact
+ * @return  <code>true</code> if rect touches this Rectangle
+ * @since 2.0
+ */
+public bool touches(Rectangle rect) {
+    return rect.x <= x + width
+        && rect.y <= y + height
+        && rect.x + rect.width >= x
+        && rect.y + rect.height >= y;
+}
+
+/**
+ * Moves this Rectangle horizontally by the x value of the given Point and
+ * vertically by the y value of the given Point, then returns this Rectangle for
+ * convenience.
+ *
+ * @param p  Point which provides translation information
+ * @return  <code>this</code> for convenience
+ */
+public Rectangle translate(Point p) {
+    x += p.x;
+    y += p.y;
+    return this;
+}
+
+/**
+ * Moves this Rectangle horizontally by dx and vertically by dy, then returns
+ * this Rectangle for convenience.
+ *
+ * @param dx  Shift along X axis
+ * @param dy  Shift along Y axis
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Rectangle translate(int dx, int dy) {
+    x += dx;
+    y += dy;
+    return this;
+}
+
+/**
+ * Switches the x and y values, as well as the width and height of this Rectangle.
+ * Useful for orientation changes.
+ *
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Rectangle transpose() {
+    int temp = x;
+    x = y;
+    y = temp;
+    temp = width;
+    width = height;
+    height = temp;
+    return this;
+}
+
+/**
+ * Unions this Rectangle's width and height with the specified Dimension.
+ *
+ * @param d Dimension being unioned
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Rectangle union_(Dimension d) {
+    width = Math.max(width, d.width);
+    height = Math.max(height, d.height);
+    return this;
+}
+
+/**
+ * Updates this Rectangle's bounds to the minimum size which can hold both this
+ * Rectangle and the coordinate (x,y).
+ *
+ * @return  <code>this</code> for convenience
+ * @param x1 X coordinate
+ * @param y1 Y coordinate
+ * @since 2.0
+ */
+public Rectangle union_(int x1, int y1) {
+    if (x1 < x) {
+        width += (x - x1);
+        x = x1;
+    } else {
+        int right = x + width;
+        if (x1 >= right) {
+            right = x1 + 1;
+            width = right - x;
+        }
+    }
+    if (y1 < y) {
+        height += (y - y1);
+        y = y1;
+    } else {
+        int bottom = y + height;
+        if (y1 >= bottom) {
+            bottom = y1 + 1;
+            height = bottom - y;
+        }
+    }
+    return this;
+}
+
+/**
+ * Updates this Rectangle's bounds to the minimum size which can hold both this
+ * Rectangle and the given Point.
+ *
+ * @param p  Point to be unioned with this Rectangle
+ * @since 2.0
+ */
+public void union_(Point p) {
+    union_(p.x, p.y);
+}
+
+/**
+ * Updates this Rectangle's dimensions to the minimum size which can hold both
+ * this Rectangle and the given Rectangle.
+ *
+ * @return  <code>this</code> for convenience
+ * @param rect  Rectangle to be unioned with this Rectangle
+ * @since 2.0
+ */
+public Rectangle union_(Rectangle rect) {
+    if (rect is null)
+        return this;
+    return union_(rect.x, rect.y, rect.width, rect.height);
+}
+
+/**
+ * Updates this Rectangle's dimensions to the minimum size which can hold both
+ * this Rectangle and the rectangle (x, y, w, h).
+ *
+ * @param x X coordiante of desired union_.
+ * @param y Y coordiante of desired union_.
+ * @param w Width of desired union_.
+ * @param h Height of desired union_.
+ * @return  <code>this</code> for convenience
+ * @since 2.0
+ */
+public Rectangle union_(int x, int y, int w, int h) {
+    int right = Math.max(this.x + width, x + w);
+    int bottom = Math.max(this.y + height, y + h);
+    this.x = Math.min(this.x, x);
+    this.y = Math.min(this.y, y);
+    this.width = right - this.x;
+    this.height = bottom - this.y;
+    return this;
+}
+
+/**
+ * Returns <code>double</code> x coordinate
+ *
+ * @return <code>double</code> x coordinate
+ * @since 3.4
+ */
+public double preciseX() {
+    return x;
+}
+
+/**
+ * Returns <code>double</code> y coordinate
+ *
+ * @return <code>double</code> y coordinate
+ * @since 3.4
+ */
+public double preciseY() {
+    return y;
+}
+
+/**
+ * Returns <code>double</code> width
+ *
+ * @return <code>double</code> width
+ * @since 3.4
+ */
+public double preciseWidth() {
+    return width;
+}
+
+/**
+ * Returns <code>double</code> height
+ *
+ * @return <code>double</code> height
+ * @since 3.4
+ */
+public double preciseHeight() {
+    return height;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/geometry/Transform.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.geometry.Transform;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.geometry.Point;
+
+/**
+ * Provides support for transformations of scaling, translation and rotation.
+ */
+public class Transform {
+
+private double
+    scaleX = 1.0,
+    scaleY = 1.0,
+    dx, dy,
+    cos = 1.0,
+    sin;
+
+/**
+ * Sets the value for the amount of scaling to be done along both axes.
+ *
+ * @param scale  Scale factor
+ * @since 2.0
+ */
+public void setScale(double scale) {
+    scaleX = scaleY = scale;
+}
+
+/**
+ * Sets the value for the amount of scaling to be done along X and Y axes
+ * individually.
+ *
+ * @param x  Amount of scaling on X axis
+ * @param y  Amount of scaling on Y axis
+ * @since 2.0
+ */
+public void setScale(double x, double y) {
+    scaleX = x;
+    scaleY = y;
+}
+
+/**
+ * Sets the rotation angle.
+ *
+ * @param angle  Angle of rotation
+ * @since 2.0
+ */
+public void setRotation(double angle) {
+    cos = Math.cos(angle);
+    sin = Math.sin(angle);
+}
+
+/**
+ * Sets the translation amounts for both axes.
+ *
+ * @param x  Amount of shift on X axis
+ * @param y  Amount of shift on Y axis
+ * @since 2.0
+ */
+public void setTranslation(double x, double y) {
+    dx = x;
+    dy = y;
+}
+
+/**
+ * Returns a new transformed Point of the input Point based on the transformation
+ * values set.
+ *
+ * @param p  Point being transformed
+ * @return  The transformed Point
+ * @since 2.0
+ */
+public Point getTransformed(Point p) {
+    double x = p.x;
+    double y = p.y;
+    double temp;
+    x *= scaleX;
+    y *= scaleY;
+
+    temp = x * cos - y * sin;
+    y    = x * sin + y * cos;
+    x = temp;
+    return new Point(Math.round(x + dx), Math.round(y + dy));
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/geometry/Translatable.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.geometry.Translatable;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A translatable object can be translated (or moved) vertically and/or horizontally.
+ */
+public interface Translatable {
+
+/**
+ * Translates this object horizontally by <code>dx</code> and vertically by 
+ * <code>dy</code>.
+ * 
+ * @param dx The amount to translate horizontally
+ * @param dy The amount to translate vertically
+ * @since 2.0
+ */
+void performTranslate(int dx, int dy);
+
+/**
+ * Scales this object by the scale factor.
+ * 
+ * @param factor The scale factor
+ * @since 2.0
+ */
+void performScale(double factor);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/geometry/Transposer.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.geometry.Transposer;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Rectangle;
+
+/**
+ * Conditionally transposes geometrical objects based on an "enabled" flag. When
+ * enabled, the method t(Object) will transpose the passed geometrical object.
+ * Otherwise, the object will be returned without modification
+ */
+public class Transposer {
+
+private bool enabled = false;
+
+/**
+ * Disables transposing of inputs.
+ *
+ * @since 2.0
+ */
+public void disable() {
+    enabled = false;
+}
+
+/**
+ * Enables transposing of inputs.
+ *
+ * @since 2.0
+ */
+public void enable() {
+    enabled = true;
+}
+
+/**
+ * Returns <code>true</code> if this Transposer is enabled.
+ *
+ * @return <code>true</code> if this Transposer is enabled
+ * @since 2.0
+ */
+public bool isEnabled() {
+    return enabled;
+}
+
+/**
+ * Sets the enabled state of this Transposer.
+ *
+ * @param e  New enabled value
+ * @since 2.0
+ */
+public void setEnabled(bool e) {
+    enabled = e;
+}
+
+/**
+ * Returns a new transposed Dimension of the input Dimension.
+ *
+ * @param d  Input dimension being transposed.
+ * @return  The new transposed dimension.
+ * @since 2.0
+ */
+public Dimension t(Dimension d) {
+    if (isEnabled())
+        return d.getTransposed();
+    return d;
+}
+
+/**
+ * Returns a new transposed Insets of the input Insets.
+ *
+ * @param i  Insets to be transposed.
+ * @return  The new transposed Insets.
+ * @since 2.0
+ */
+public Insets t(Insets i) {
+    if (isEnabled())
+        return i.getTransposed();
+    return i;
+}
+
+/**
+ * Returns a new transposed Point of the input Point.
+ *
+ * @param p  Point to be transposed.
+ * @return  The new transposed Point.
+ * @since 2.0
+ */
+public Point t(Point p) {
+    if (isEnabled())
+        return p.getTransposed();
+    return p;
+}
+
+/**
+ * Returns a new transposed Rectangle of the input Rectangle.
+ *
+ * @param r  Rectangle to be transposed.
+ * @return  The new trasnposed Rectangle.
+ * @since 2.0
+ */
+public Rectangle t(Rectangle r) {
+    if (isEnabled())
+        return r.getTransposed();
+    return r;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/BreakCycles.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,280 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.BreakCycles;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.draw2d.graph.GraphVisitor;
+import dwtx.draw2d.graph.NodeList;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.Edge;
+
+/**
+ * This visitor eliminates cycles in the graph using a "greedy" heuristic.  Nodes which
+ * are sources and sinks are marked and placed in a source and sink list, leaving only
+ * nodes involved in cycles. A remaining node with the highest (outgoing-incoming) edges
+ * score is then chosen greedily as if it were a source. The process is repeated until all
+ * nodes have been marked and placed in a list.  The lists are then concatenated, and any
+ * edges which go backwards in this list will be inverted during the layout procedure.
+ *
+ * @author Daniel Lee
+ * @since 2.1.2
+ */
+class BreakCycles : GraphVisitor {
+
+// Used in identifying cycles and in cycle removal.
+// Flag field indicates "presence". If true, the node has been removed from the list.
+NodeList graphNodes;
+
+public this(){
+    graphNodes = new NodeList();
+}
+
+private bool allNodesFlagged() {
+    for (int i = 0; i < graphNodes.size(); i++) {
+        if (graphNodes.getNode(i).flag is false)
+            return false;
+    }
+    return true;
+}
+
+private void breakCycles(DirectedGraph g) {
+    initializeDegrees(g);
+    greedyCycleRemove(g);
+    invertEdges(g);
+}
+
+/*
+ * Returns true if g contains cycles, false otherwise
+ */
+private bool containsCycles(DirectedGraph g) {
+    List noLefts = new ArrayList();
+    //Identify all initial nodes for removal
+    for (int i = 0; i < graphNodes.size(); i++) {
+        Node node = graphNodes.getNode(i);
+        if (getIncomingCount(node) is 0)
+            sortedInsert(noLefts, node);
+    }
+
+    while (noLefts.size() > 0) {
+        Node node = cast(Node)noLefts.remove(noLefts.size() - 1);
+        node.flag = true;
+        for (int i = 0; i < node.outgoing.size(); i++) {
+            Node right = node.outgoing.getEdge(i).target;
+            setIncomingCount(right, getIncomingCount(right) - 1);
+            if (getIncomingCount(right) is 0)
+                sortedInsert(noLefts, right);
+        }
+    }
+
+    if (allNodesFlagged())
+        return false;
+    return true;
+}
+
+/*
+ * Returns the node in graphNodes with the largest
+ * (outgoing edge count - incoming edge count) value
+ */
+private Node findNodeWithMaxDegree() {
+    int max = Integer.MIN_VALUE;
+    Node maxNode = null;
+
+    for (int i = 0; i < graphNodes.size(); i++) {
+        Node node = graphNodes.getNode(i);
+        if (getDegree(node) >= max && node.flag is false) {
+            max = getDegree(node);
+            maxNode = node;
+        }
+    }
+    return maxNode;
+}
+
+private int getDegree(Node n) {
+    return n.workingInts[3];
+}
+
+private int getIncomingCount(Node n) {
+    return n.workingInts[0];
+}
+
+private int getInDegree(Node n) {
+    return n.workingInts[1];
+}
+
+private int getOrderIndex(Node n) {
+    return n.workingInts[0];
+}
+
+private int getOutDegree(Node n) {
+    return n.workingInts[2];
+}
+
+private void greedyCycleRemove(DirectedGraph g) {
+    NodeList sL = new NodeList();
+    NodeList sR = new NodeList();
+
+    do {
+        // Add all sinks and isolated nodes to sR
+        bool hasSink;
+        do {
+            hasSink = false;
+            for (int i = 0; i < graphNodes.size(); i++) {
+                Node node = graphNodes.getNode(i);
+                if (getOutDegree(node) is 0 && node.flag is false) {
+                    hasSink = true;
+                    node.flag = true;
+                    updateIncoming(node);
+                    sR.add(node);
+                    break;
+                }
+            }
+        } while (hasSink);
+
+        // Add all sources to sL
+        bool hasSource;
+        do {
+            hasSource = false;
+            for (int i = 0; i < graphNodes.size(); i++) {
+                Node node = graphNodes.getNode(i);
+                if (getInDegree(node) is 0 && node.flag is false) {
+                    hasSource = true;
+                    node.flag = true;
+                    updateOutgoing(node);
+                    sL.add(node);
+                    break;
+                }
+            }
+        } while (hasSource);
+
+        // When all sinks and sources are removed, choose a node with the
+        // maximum degree (outDegree - inDegree) and add it to sL
+        Node max = findNodeWithMaxDegree();
+        if (max !is null) {
+            sL.add(max);
+            max.flag = true;
+            updateIncoming(max);
+            updateOutgoing(max);
+        }
+    } while (!allNodesFlagged());
+
+    // Assign order indexes
+    int orderIndex = 0;
+    for (int i = 0; i < sL.size(); i++) {
+        setOrderIndex(sL.getNode(i), orderIndex++);
+    }
+    for (int i = sR.size() - 1; i >= 0; i--) {
+        setOrderIndex(sR.getNode(i), orderIndex++);
+    }
+}
+
+private void initializeDegrees(DirectedGraph g) {
+    graphNodes.resetFlags();
+    for (int i = 0; i < g.nodes.size(); i++) {
+        Node n = graphNodes.getNode(i);
+        setInDegree(n, n.incoming.size());
+        setOutDegree(n, n.outgoing.size());
+        setDegree(n, n.outgoing.size() - n.incoming.size());
+    }
+}
+
+private void invertEdges(DirectedGraph g) {
+    for (int i = 0; i < g.edges.size(); i++) {
+        Edge e = g.edges.getEdge(i);
+        if (getOrderIndex(e.source) > getOrderIndex(e.target)) {
+            e.invert();
+            e.isFeedback_ = true;
+        }
+    }
+}
+
+private void setDegree(Node n, int deg) {
+    n.workingInts[3] = deg;
+}
+
+private void setIncomingCount(Node n, int count) {
+    n.workingInts[0] = count;
+}
+
+private void setInDegree(Node n, int deg) {
+    n.workingInts[1] = deg;
+}
+
+private void setOutDegree(Node n, int deg) {
+    n.workingInts[2] = deg;
+}
+
+private void setOrderIndex(Node n, int index) {
+    n.workingInts[0] = index;
+}
+
+private void sortedInsert(List list, Node node) {
+    int insert = 0;
+    while (insert < list.size()
+      && (cast(Node)list.get(insert)).sortValue > node.sortValue)
+        insert++;
+    list.add(insert, node);
+}
+
+/*
+ * Called after removal of n. Updates the degree values of n's incoming nodes.
+ */
+private void updateIncoming(Node n) {
+    for (int i = 0; i < n.incoming.size(); i++) {
+        Node in_ = n.incoming.getEdge(i).source;
+        if (in_.flag is false) {
+            setOutDegree(in_, getOutDegree(in_) - 1);
+            setDegree(in_, getOutDegree(in_) - getInDegree(in_));
+        }
+    }
+}
+
+/*
+ * Called after removal of n. Updates the degree values of n's outgoing nodes.
+ */
+private void updateOutgoing(Node n) {
+    for (int i = 0; i < n.outgoing.size(); i++) {
+        Node out_ = n.outgoing.getEdge(i).target;
+        if (out_.flag is false) {
+            setInDegree(out_, getInDegree(out_) - 1);
+            setDegree(out_, getOutDegree(out_) - getInDegree(out_));
+        }
+    }
+}
+
+public void revisit(DirectedGraph g) {
+    for (int i = 0; i < g.edges.size(); i++) {
+        Edge e = g.edges.getEdge(i);
+        if (e.isFeedback())
+            e.invert();
+    }
+}
+
+/**
+ * @see GraphVisitor#visit(dwtx.draw2d.graph.DirectedGraph)
+ */
+public void visit(DirectedGraph g) {
+    // put all nodes in list, initialize index
+    graphNodes.resetFlags();
+    for (int i = 0; i < g.nodes.size(); i++) {
+        Node n = g.nodes.getNode(i);
+        setIncomingCount(n, n.incoming.size());
+        graphNodes.add(n);
+    }
+    if (containsCycles(g)) {
+        breakCycles(g);
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/Cell.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.graph.Cell;
+
+import dwt.dwthelper.utils;
+
+public class Cell {
+
+public int index;
+
+public int rank;
+
+public this(int rank, int index){
+    this.rank = rank;
+    this.index = index;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/CollapsedEdges.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.CollapsedEdges;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.graph.Edge;
+
+/**
+ * Contains the information from all edges going from a given cluster to some other
+ * cluster.  An edge with minimal slack as chosen to maintain the link between clusters.
+ * The weight and any slack more than the minimal edge's slack is tracked for all other
+ * edges.
+ * @since 3.1
+ */
+class CollapsedEdges {
+
+    /**
+     * The total weight of the collapsed edges.
+     */
+    int collapsedWeight;
+    int collapsedCount;
+
+    /**
+     * The total amount of weighted difference in the collapsed edges slack and the
+     * tightest edge's slack.
+     */
+    int overage;
+    int unOverage;
+    Edge tightestEdge;
+
+    this(Edge edge) {
+        tightestEdge = edge;
+        collapsedWeight = edge.weight;
+        collapsedCount++;
+    }
+
+    public int getWeightedPull() {
+        return tightestEdge.getSlack() * collapsedWeight + overage;
+    }
+
+    public bool isTight() {
+        return tightestEdge.getSlack() is 0;
+    }
+
+    /**
+     * Compares the given edge to the current tightest edge.  If the given edge is tighter
+     * than the current, the current tightest is returned.  Otherwise, the edge itself is
+     * returned.  The returned edge would be the one to remove from the graph.
+     * @param candidate another edge
+     * @return the edge which is not the tightest edge
+     * @since 3.1
+     */
+    Edge processEdge(Edge candidate) {
+        collapsedCount++;
+        if (candidate.getSlack() < tightestEdge.getSlack()) {
+            overage += collapsedWeight * (tightestEdge.getSlack() - candidate.getSlack());
+            Edge temp = tightestEdge;
+            tightestEdge = candidate;
+            collapsedWeight += candidate.weight;
+            return temp;
+        } else {
+            int over = candidate.getSlack() - tightestEdge.getSlack();
+            unOverage += over;
+            overage += candidate.weight * over;
+            collapsedWeight += candidate.weight;
+            return candidate;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/CompoundBreakCycles.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,481 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.CompoundBreakCycles;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.graph.GraphVisitor;
+import dwtx.draw2d.graph.NodeList;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.Subgraph;
+import dwtx.draw2d.graph.Edge;
+
+
+/**
+ * This visitor eliminates cycles in the graph via a modified implementation of the
+ * greedy cycle removal algorithm for directed graphs. The algorithm has been modified to
+ * handle the presence of Subgraphs and compound cycles which may result. This algorithm
+ * determines a set of edges which can be inverted and result in a graph without compound
+ * cycles.
+ *
+ * @author Daniel Lee
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+class CompoundBreakCycles : GraphVisitor {
+
+/*
+ * Caches all nodes in the graph. Used in identifying cycles and in cycle removal.
+ * Flag field indicates "presence". If true, the node has been removed from the list.
+ */
+private NodeList graphNodes;
+private NodeList sL;
+
+public this(){
+    sL = new NodeList();
+}
+
+private bool allFlagged(NodeList nodes) {
+    for (int i = 0; i < nodes.size(); i++) {
+        if (nodes.getNode(i).flag is false)
+            return false;
+    }
+    return true;
+}
+
+private int buildNestingTreeIndices(NodeList nodes, int base) {
+    for (int i = 0; i < nodes.size(); i++) {
+        Node node = cast(Node)nodes.get(i);
+        if (auto s = cast(Subgraph) node ) {
+            s.nestingTreeMin = base;
+            base = buildNestingTreeIndices(s.members, base);
+        }
+        node.nestingIndex = base++;
+    }
+    return base++;
+}
+
+private bool canBeRemoved(Node n) {
+    return !n.flag && getChildCount(n) is 0;
+}
+
+private bool changeInDegree(Node n, int delta) {
+    return (n.workingInts[1] += delta) is 0;
+}
+
+private bool changeOutDegree(Node n, int delta) {
+    return (n.workingInts[2] += delta) is 0;
+}
+
+/*
+ * Execution of the modified greedy cycle removal algorithm.
+ */
+private void cycleRemove(NodeList children) {
+    NodeList sR = new NodeList();
+    do {
+        findSinks(children, sR);
+        findSources(children);
+
+        // all sinks and sources added, find node with highest
+        // outDegree - inDegree
+        Node max = findNodeWithMaxDegree(children);
+        if (max !is null) {
+            for (int i = 0; i < children.size(); i++) {
+                Node child = cast(Node)children.get(i);
+                if (child.flag)
+                    continue;
+                if (child is max)
+                    restoreSinks(max, sR);
+                else
+                    restoreSources(child);
+            }
+            remove(max);
+        }
+    } while (!allFlagged(children));
+    while (!sR.isEmpty())
+        sL.add(sR.remove(sR.size() - 1));
+}
+
+private void findInitialSinks(NodeList children, NodeList sinks) {
+    for (int i = 0; i < children.size(); i++) {
+        Node node = children.getNode(i);
+        if (node.flag)
+            continue;
+        if (isSink(node) && canBeRemoved(node)) {
+            sinks.add(node);
+            node.flag = true;
+        }
+        if (null !is cast(Subgraph)node )
+            findInitialSinks((cast(Subgraph)node).members, sinks);
+    }
+}
+
+private void findInitialSources(NodeList children, NodeList sources) {
+    for (int i = 0; i < children.size(); i++) {
+        Node node = children.getNode(i);
+        if (isSource(node) && canBeRemoved(node)) {
+            sources.add(node);
+            node.flag = true;
+        }
+        if (null !is cast(Subgraph)node )
+            findInitialSources((cast(Subgraph)node).members, sources);
+    }
+}
+
+private Node findNodeWithMaxDegree(NodeList nodes) {
+    int max = Integer.MIN_VALUE;
+    Node maxNode = null;
+
+    for (int i = 0; i < nodes.size(); i++) {
+        Node node = nodes.getNode(i);
+        if (node.flag)
+            continue;
+        int degree = getNestedOutDegree(node) - getNestedInDegree(node);
+        if (degree >= max && node.flag is false) {
+            max = degree;
+            maxNode = node;
+        }
+    }
+    return maxNode;
+}
+
+/*
+ * Finds all sinks in graphNodes and adds them to the passed NodeList
+ */
+private void findSinks(NodeList children, NodeList rightList) {
+//  NodeList rightList = new NodeList();
+    NodeList sinks = new NodeList();
+    findInitialSinks(children, sinks);
+    while (!sinks.isEmpty()) {
+        Node sink = sinks.getNode(sinks.size() - 1);
+        rightList.add(sink);
+        sinks.remove(sink);
+        removeSink(sink, sinks);
+
+        // Check to see if the removal has made the parent node a sink
+        if (sink.getParent() !is null) {
+            Node parent = sink.getParent();
+            setChildCount(parent, getChildCount(parent) - 1);
+            if (isSink(parent) && canBeRemoved(parent)) {
+                sinks.add(parent);
+                parent.flag = true;
+            }
+        }
+    }
+}
+
+/*
+ * Finds all sources in graphNodes and adds them to the sL NodeList.
+ */
+private void findSources(NodeList children) {
+    NodeList sources = new NodeList();
+    findInitialSources(children, sources);
+    while (!sources.isEmpty()) {
+        Node source = sources.getNode(sources.size() - 1);
+        sL.add(source);
+        sources.remove(source);
+        removeSource(source, sources);
+
+        // Check to see if the removal has made the parent node a source
+        if (source.getParent() !is null) {
+            Node parent = source.getParent();
+            setChildCount(parent, getChildCount(parent) - 1);
+            if (isSource(parent) && canBeRemoved(parent)) {
+                sources.add(parent);
+                parent.flag = true;
+            }
+        }
+    }
+}
+
+private int getChildCount(Node n) {
+    return n.workingInts[3];
+}
+
+private int getInDegree(Node n) {
+    return n.workingInts[1];
+}
+
+private int getNestedInDegree(Node n) {
+    int result = getInDegree(n);
+    if ( auto s = cast(Subgraph)n ) {
+        for (int i = 0; i < s.members.size(); i++)
+            if (!s.members.getNode(i).flag)
+                result += getInDegree(s.members.getNode(i));
+    }
+    return result;
+}
+
+private int getNestedOutDegree(Node n) {
+    int result = getOutDegree(n);
+    if ( auto s = cast(Subgraph)n ) {
+        for (int i = 0; i < s.members.size(); i++)
+            if (!s.members.getNode(i).flag)
+                result += getOutDegree(s.members.getNode(i));
+    }
+    return result;
+}
+
+private int getOrderIndex(Node n) {
+    return n.workingInts[0];
+}
+
+private int getOutDegree(Node n) {
+    return n.workingInts[2];
+}
+
+private void initializeDegrees(DirectedGraph g) {
+    g.nodes.resetFlags();
+    g.edges.resetFlags(false);
+    for (int i = 0; i < g.nodes.size(); i++) {
+        Node n = g.nodes.getNode(i);
+        setInDegree(n, n.incoming.size());
+        setOutDegree(n, n.outgoing.size());
+        if ( auto s = cast(Subgraph)n )
+            setChildCount(n, s.members.size());
+        else
+            setChildCount(n, 0);
+    }
+}
+
+private void invertEdges(DirectedGraph g) {
+    // Assign order indices
+    int orderIndex = 0;
+    for (int i = 0; i < sL.size(); i++) {
+        setOrderIndex(sL.getNode(i), orderIndex++);
+    }
+    // Invert edges that are causing a cycle
+    for (int i = 0; i < g.edges.size(); i++) {
+        Edge e = g.edges.getEdge(i);
+        if (getOrderIndex(e.source) > getOrderIndex(e.target)
+          && !e.source.isNested(e.target)
+          && !e.target.isNested(e.source)) {
+            e.invert();
+            e.isFeedback_ = true;
+        }
+    }
+}
+
+/**
+ * Removes all edges connecting the given subgraph to other nodes outside of it.
+ * @param s
+ * @param n
+ */
+private void isolateSubgraph(Subgraph subgraph, Node member) {
+    Edge edge = null;
+    for (int i = 0; i < member.incoming.size(); i++) {
+        edge = member.incoming.getEdge(i);
+        if (!subgraph.isNested(edge.source) && !edge.flag)
+            removeEdge(edge);
+    }
+    for (int i = 0; i < member.outgoing.size(); i++) {
+        edge = member.outgoing.getEdge(i);
+        if (!subgraph.isNested(edge.target) && !edge.flag)
+            removeEdge(edge);
+    }
+    if ( auto s = cast(Subgraph)member ) {
+        NodeList members = s.members;
+        for (int i = 0; i < members.size(); i++)
+            isolateSubgraph(subgraph, members.getNode(i));
+    }
+}
+
+private bool isSink(Node n) {
+    return getOutDegree(n) is 0
+        && (n.getParent() is null
+            || isSink(n.getParent()));
+}
+
+private bool isSource(Node n) {
+    return  getInDegree(n) is 0
+        && (n.getParent() is null
+            || isSource(n.getParent()));
+}
+
+private void remove(Node n) {
+    n.flag = true;
+    if (n.getParent() !is null)
+        setChildCount(n.getParent(), getChildCount(n.getParent()) - 1);
+    removeSink(n, null);
+    removeSource(n, null);
+    sL.add(n);
+    if ( auto s = cast(Subgraph)n ) {
+        isolateSubgraph(s, s);
+        cycleRemove(s.members);
+    }
+}
+
+private bool removeEdge(Edge e) {
+    if (e.flag)
+        return false;
+    e.flag = true;
+    changeOutDegree(e.source, -1);
+    changeInDegree(e.target, -1);
+    return true;
+}
+
+/**
+ * Removes all edges between a parent and any of its children or descendants.
+ */
+private void removeParentChildEdges(DirectedGraph g) {
+    for (int i = 0; i < g.edges.size(); i++) {
+        Edge e = g.edges.getEdge(i);
+        if (e.source.isNested(e.target) || e.target.isNested(e.source))
+            removeEdge(e);
+    }
+}
+
+private void removeSink(Node sink, NodeList allSinks) {
+    for (int i = 0; i < sink.incoming.size(); i++) {
+        Edge e = sink.incoming.getEdge(i);
+        if (!e.flag) {
+            removeEdge(e);
+            Node source = e.source;
+            if (allSinks !is null && isSink(source) && canBeRemoved(source)) {
+                allSinks.add(source);
+                source.flag = true;
+            }
+        }
+    }
+}
+
+private void removeSource(Node n, NodeList allSources) {
+    for (int i = 0; i < n.outgoing.size(); i++) {
+        Edge e = n.outgoing.getEdge(i);
+        if (!e.flag) {
+            e.flag = true;
+            changeInDegree(e.target, -1);
+            changeOutDegree(e.source, -1);
+
+            Node target = e.target;
+            if (allSources !is null && isSource(target) && canBeRemoved(target)) {
+                allSources.add(target);
+                target.flag = true;
+            }
+        }
+    }
+}
+
+/**
+ * Restores an edge if it has been removed, and both of its nodes are not removed.
+ * @param e the edge
+ * @return <code>true</code> if the edge was restored
+ */
+private bool restoreEdge(Edge e) {
+    if (!e.flag || e.source.flag || e.target.flag)
+        return false;
+    e.flag = false;
+    changeOutDegree(e.source, 1);
+    changeInDegree(e.target, 1);
+    return true;
+}
+
+/**
+ * Brings back all nodes nested in the given node.
+ * @param node the node to restore
+ * @param sr current sinks
+ */
+private void restoreSinks(Node node, NodeList sR) {
+    if (node.flag && sR.contains(node)) {
+        node.flag = false;
+        if (node.getParent() !is null)
+            setChildCount(node.getParent(), getChildCount(node.getParent()) + 1);
+        sR.remove(node);
+        for (int i = 0; i < node.incoming.size(); i++) {
+            Edge e = node.incoming.getEdge(i);
+            restoreEdge(e);
+        }
+        for (int i = 0; i < node.outgoing.size(); i++) {
+            Edge e = node.outgoing.getEdge(i);
+            restoreEdge(e);
+        }
+    }
+    if ( auto s = cast(Subgraph)node ) {
+        for (int i = 0; i < s.members.size(); i++) {
+            Node member = s.members.getNode(i);
+            restoreSinks(member, sR);
+        }
+    }
+}
+
+/**
+ * Brings back all nodes nested in the given node.
+ * @param node the node to restore
+ * @param sr current sinks
+ */
+private void restoreSources(Node node) {
+    if (node.flag && sL.contains(node)) {
+        node.flag = false;
+        if (node.getParent() !is null)
+            setChildCount(node.getParent(), getChildCount(node.getParent()) + 1);
+        sL.remove(node);
+        for (int i = 0; i < node.incoming.size(); i++) {
+            Edge e = node.incoming.getEdge(i);
+            restoreEdge(e);
+        }
+        for (int i = 0; i < node.outgoing.size(); i++) {
+            Edge e = node.outgoing.getEdge(i);
+            restoreEdge(e);
+        }
+    }
+    if ( auto s = cast(Subgraph)node ) {
+        for (int i = 0; i < s.members.size(); i++) {
+            Node member = s.members.getNode(i);
+            restoreSources(member);
+        }
+    }
+}
+
+public void revisit(DirectedGraph g) {
+    for (int i = 0; i < g.edges.size(); i++) {
+        Edge e = g.edges.getEdge(i);
+        if (e.isFeedback())
+            e.invert();
+    }
+}
+
+private void setChildCount(Node n, int count) {
+    n.workingInts[3] = count;
+}
+
+private void setInDegree(Node n, int deg) {
+    n.workingInts[1] = deg;
+}
+
+private void setOrderIndex(Node n, int index) {
+    n.workingInts[0] = index;
+}
+
+private void setOutDegree(Node n, int deg) {
+    n.workingInts[2] = deg;
+}
+
+/**
+ * @see GraphVisitor#visit(dwtx.draw2d.graph.DirectedGraph)
+ */
+public void visit(DirectedGraph g) {
+    initializeDegrees(g);
+    graphNodes = g.nodes;
+
+    NodeList roots = new NodeList();
+    for (int i = 0; i < graphNodes.size(); i++) {
+        if (graphNodes.getNode(i).getParent() is null)
+            roots.add(graphNodes.getNode(i));
+    }
+    buildNestingTreeIndices(roots, 0);
+    removeParentChildEdges(g);
+    cycleRemove(roots);
+    invertEdges(g);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/CompoundDirectedGraph.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.CompoundDirectedGraph;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.NodeList;
+import dwtx.draw2d.graph.EdgeList;
+
+/**
+ * A <code>DirectedGraph</code> whose Nodes may be compound {@link Subgraph}s, which may
+ * contain other nodes. Any node in the graph may be parented by one subgraph. Since
+ * subgraphs are nodes, the source or target end of an {@link Edge} may be a subgraph.
+ * For additional restrictions, refer to the JavaDoc for the layout algorithm being used.
+ * <P>
+ * A CompoundDirectedGraph is passed to a graph layout, which will position all of the
+ * nodes, subgraphs, and edges in that graph.  This class serves as the data structure for
+ * a layout algorithm.
+ *
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+public class CompoundDirectedGraph : DirectedGraph {
+
+/**
+ * For internal use only.
+ */
+public NodeList subgraphs;
+
+/**
+ * For internal use only.
+ */
+public EdgeList containment;
+
+public this(){
+    subgraphs = new NodeList();
+    containment = new EdgeList();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/CompoundDirectedGraphLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.CompoundDirectedGraphLayout;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.graph.DirectedGraphLayout;
+import dwtx.draw2d.graph.TransposeMetrics;
+import dwtx.draw2d.graph.CompoundBreakCycles;
+import dwtx.draw2d.graph.RouteEdges;
+import dwtx.draw2d.graph.ConvertCompoundGraph;
+import dwtx.draw2d.graph.InitialRankSolver;
+import dwtx.draw2d.graph.TightSpanningTreeSolver;
+import dwtx.draw2d.graph.RankAssignmentSolver;
+import dwtx.draw2d.graph.CompoundPopulateRanks;
+import dwtx.draw2d.graph.CompoundVerticalPlacement;
+import dwtx.draw2d.graph.MinCross;
+import dwtx.draw2d.graph.CompoundRankSorter;
+import dwtx.draw2d.graph.SortSubgraphs;
+import dwtx.draw2d.graph.CompoundHorizontalPlacement;
+
+/**
+ * Performs a graph layout on a <code>CompoundDirectedGraph</code>.  The input format is
+ * the same as for {@link DirectedGraphLayout}.  All nodes, including subgraphs and their
+ * children, should be added to the {@link DirectedGraph#nodes} field.
+ * <P>
+ * The requirements for this algorithm are the same as those of
+ * <code>DirectedGraphLayout</code>, with the following exceptions:
+ * <UL>
+ *    <LI>There is an implied edge between a subgraph and each of its member nodes. These
+ *    edges form the containment graph <EM>T</EM>. Thus, the compound directed graph
+ *    <EM>CG</EM> is said to be connected iff Union(<EM>G</EM>, <EM>T</EM>) is connected,
+ *    where G represents the given nodes (including subgraphs) and edges.
+ *
+ *    <LI>This algorithm will remove any compound cycles found in the input graph
+ *    <em>G</em> by inverting edges according to a heuristic until no more cycles are
+ *    found. A compound cycle is defined as: a cycle comprised of edges from <EM>G</EM>,
+ *    <EM>T</EM>, and <em>T<SUP>-1</SUP></em>, in the form
+ *    (c<SUP>*</SUP>e<SUP>+</SUP>p<SUP>*</SUP>e<SUP>+</SUP>)*, where
+ *    <em>T<SUP>-1</SUP></em> is the backwards graph of <EM>T</EM>, c element of T, e
+ *    element of G, and p element of T<SUP>-1</SUP>.
+ * </UL>
+ *
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+public final class CompoundDirectedGraphLayout : DirectedGraphLayout {
+
+void init() {
+    steps.add(new TransposeMetrics());
+    steps.add(new CompoundBreakCycles());
+    steps.add(new RouteEdges());
+    steps.add(new ConvertCompoundGraph());
+    steps.add(new InitialRankSolver());
+    steps.add(new TightSpanningTreeSolver());
+    steps.add(new RankAssignmentSolver());
+    steps.add(new CompoundPopulateRanks());
+    steps.add(new CompoundVerticalPlacement());
+    steps.add(new MinCross(new CompoundRankSorter()));
+    steps.add(new SortSubgraphs());
+    steps.add(new CompoundHorizontalPlacement());
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/CompoundHorizontalPlacement.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.CompoundHorizontalPlacement;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.draw2d.graph.HorizontalPlacement;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.RankList;
+import dwtx.draw2d.graph.Rank;
+import dwtx.draw2d.graph.Subgraph;
+import dwtx.draw2d.graph.SubgraphBoundary;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.NodeList;
+import dwtx.draw2d.graph.CompoundDirectedGraph;
+import dwtx.draw2d.graph.Edge;
+import dwtx.draw2d.graph.GraphUtilities;
+
+/**
+ * Calculates the X-coordinates for nodes in a compound directed graph.
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+class CompoundHorizontalPlacement : HorizontalPlacement {
+
+class LeftRight {
+    //$TODO Delete and use NodePair class, equivalent
+    Object left, right;
+    this(Object l, Object r) {
+        left = l; right = r;
+    }
+    public override int opEquals(Object obj) {
+        LeftRight entry = cast(LeftRight)obj;
+        return entry.left.opEquals(left) && entry.right.opEquals(right);
+    }
+    public override hash_t toHash() {
+        return left.toHash() ^ right.toHash();
+    }
+}
+
+Set entries;
+
+public this(){
+    entries = new HashSet();
+}
+
+/**
+ * @see org.eclipse.graph.HorizontalPlacement#applyGPrime()
+ */
+void applyGPrime() {
+    super.applyGPrime();
+    NodeList subgraphs = (cast(CompoundDirectedGraph)graph).subgraphs;
+    for (int i = 0; i < subgraphs.size(); i++) {
+        Subgraph s = cast(Subgraph)subgraphs.get(i);
+        s.x = s.left.x;
+        s.width = s.right.x + s.right.width - s.x;
+    }
+}
+
+/**
+ * @see HorizontalPlacement#buildRankSeparators(RankList)
+ */
+void buildRankSeparators(RankList ranks) {
+    CompoundDirectedGraph g = cast(CompoundDirectedGraph)graph;
+
+    Rank rank;
+    for (int row = 0; row < g.ranks.size(); row++) {
+        rank = g.ranks.getRank(row);
+        Node n = null, prev = null;
+        for (int j = 0; j < rank.size(); j++) {
+            n = rank.getNode(j);
+            if (prev is null) {
+                Node left = addSeparatorsLeft(n, null);
+                if (left !is null) {
+                    Edge e = new Edge(graphLeft, getPrime(left), 0, 0);
+                    prime.edges.add(e);
+                    e.delta = graph.getPadding(n).left + graph.getMargin().left;
+                }
+
+            } else {
+                Subgraph s = GraphUtilities.getCommonAncestor(prev, n);
+                Node left = addSeparatorsRight(prev, s);
+                Node right = addSeparatorsLeft(n, s);
+                createEdge(left, right);
+            }
+            prev = n;
+        }
+        if (n !is null)
+            addSeparatorsRight(n, null);
+    }
+}
+
+void createEdge(Node left, Node right) {
+    LeftRight entry = new LeftRight(left, right);
+    if (entries.contains(entry))
+        return;
+    entries.add(entry);
+    int separation = left.width
+            + graph.getPadding(left).right
+            + graph.getPadding(right).left;
+    prime.edges.add(new Edge(
+        getPrime(left), getPrime(right), separation, 0
+    ));
+}
+
+Node addSeparatorsLeft(Node n, Subgraph graph) {
+    Subgraph parent = n.getParent();
+    while (parent !is graph && parent !is null) {
+        createEdge(getLeft(parent), n);
+        n = parent.left;
+        parent = parent.getParent();
+    }
+    return n;
+}
+
+Node addSeparatorsRight(Node n, Subgraph graph) {
+    Subgraph parent = n.getParent();
+    while (parent !is graph && parent !is null) {
+        createEdge(n, getRight(parent));
+        n = parent.right;
+        parent = parent.getParent();
+    }
+    return n;
+}
+
+Node getLeft(Subgraph s) {
+    if (s.left is null) {
+        s.left = new SubgraphBoundary(s, graph.getPadding(s), 1);
+        s.left.rank = (s.head.rank + s.tail.rank) / 2;
+
+        Node head = getPrime(s.head);
+        Node tail = getPrime(s.tail);
+        Node left = getPrime(s.left);
+        Node right = getPrime(getRight(s));
+        prime.edges.add(new Edge(left, right, s.width, 0));
+        prime.edges.add(new Edge(left, head, 0, 1));
+        prime.edges.add(new Edge(head, right, 0, 1));
+        prime.edges.add(new Edge(left, tail, 0, 1));
+        prime.edges.add(new Edge(tail, right, 0, 1));
+    }
+    return s.left;
+}
+
+Node getRight(Subgraph s) {
+    if (s.right is null) {
+        s.right = new SubgraphBoundary(s, graph.getPadding(s), 3);
+        s.right.rank = (s.head.rank + s.tail.rank) / 2;
+    }
+    return s.right;
+}
+
+Node getPrime(Node n) {
+    Node nPrime = get(n);
+    if (nPrime is null) {
+        nPrime = new Node(n);
+        prime.nodes.add(nPrime);
+        map(n, nPrime);
+    }
+    return nPrime;
+}
+
+public void visit(DirectedGraph g) {
+    super.visit(g);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/CompoundPopulateRanks.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.CompoundPopulateRanks;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.draw2d.graph.PopulateRanks;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.Subgraph;
+import dwtx.draw2d.graph.CompoundDirectedGraph;
+import dwtx.draw2d.graph.Edge;
+import dwtx.draw2d.graph.NodeList;
+import dwtx.draw2d.graph.Node;
+
+/**
+ * Places nodes into ranks for a compound directed graph.  If a subgraph spans a rank
+ * without any nodes which belong to that rank, a bridge node is inserted to prevent nodes
+ * from violating the subgraph boundary.
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+class CompoundPopulateRanks : PopulateRanks {
+
+public void visit(DirectedGraph g) {
+    CompoundDirectedGraph graph = cast(CompoundDirectedGraph)g;
+
+    /**
+     * Remove long containment edges at this point so they don't affect MinCross.
+     */
+    Iterator containment = graph.containment.iterator();
+    while (containment.hasNext()) {
+        Edge e = cast(Edge)containment.next();
+        if (e.getSlack() > 0) {
+            graph.removeEdge(e);
+            containment.remove();
+        }
+    }
+
+    super.visit(g);
+    NodeList subgraphs = graph.subgraphs;
+    for (int i = 0; i < subgraphs.size(); i++) {
+        Subgraph subgraph = cast(Subgraph)subgraphs.get(i);
+        bridgeSubgraph(subgraph, graph);
+    }
+}
+
+/**
+ * @param subgraph
+ */
+private void bridgeSubgraph(Subgraph subgraph, CompoundDirectedGraph g) {
+    int offset = subgraph.head.rank;
+    bool occupied[] = new bool[subgraph.tail.rank - subgraph.head.rank + 1];
+    Node bridge[] = new Node[occupied.length];
+
+    for (int i = 0; i < subgraph.members.size(); i++) {
+        Node n = cast(Node)subgraph.members.get(i);
+        if (auto s = cast(Subgraph)n ) {
+            for (int r = s.head.rank; r <= s.tail.rank; r++)
+                occupied[r - offset] = true;
+        } else
+            occupied[n.rank - offset] = true;
+    }
+
+    for (int i = 0; i < bridge.length; i++) {
+        if (!occupied[i]) {
+            Node br = bridge[i] = new Node(stringcast("bridge"), subgraph); //$NON-NLS-1$
+            br.rank = i + offset;
+            br.height = br.width = 0;
+            br.nestingIndex = subgraph.nestingIndex;
+            g.ranks.getRank(br.rank).add(br);
+            g.nodes.add(br);
+        }
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/CompoundRankSorter.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,238 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.CompoundRankSorter;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.draw2d.graph.RankSorter;
+import dwtx.draw2d.graph.Subgraph;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.Rank;
+import dwtx.draw2d.graph.NestingTree;
+import dwtx.draw2d.graph.CompoundDirectedGraph;
+import dwtx.draw2d.graph.Edge;
+import dwtx.draw2d.graph.LocalOptimizer;
+
+/**
+ * Sorts nodes in a compound directed graph.
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+class CompoundRankSorter : RankSorter {
+
+static class RowEntry {
+    double contribution;
+    int count;
+    void reset() {
+        count = 0;
+        contribution = 0;
+    }
+}
+
+static class RowKey {
+    int rank;
+    Subgraph s;
+    this() { }
+    this(Subgraph s, int rank) {
+        this.s = s;
+        this.rank = rank;
+    }
+
+    public override int opEquals(Object obj) {
+        RowKey rp = cast(RowKey)obj;
+        return rp.s is s && rp.rank is rank;
+    }
+
+    public override hash_t toHash() {
+        return s.toHash() ^ (rank * 31);
+    }
+}
+
+bool init_;
+RowKey key;
+
+Map map;
+
+public this(){
+    key = new RowKey();
+    map = new HashMap();
+}
+
+void addRowEntry(Subgraph s, int row) {
+    key.s = s;
+    key.rank = row;
+    if (!map.containsKey(key))
+        map.put(new RowKey(s, row), new RowEntry());
+}
+
+protected void assignIncomingSortValues() {
+    super.assignIncomingSortValues();
+    pullTogetherSubgraphs();
+}
+
+protected void assignOutgoingSortValues() {
+    super.assignOutgoingSortValues();
+    pullTogetherSubgraphs();
+}
+
+void optimize(DirectedGraph g) {
+    CompoundDirectedGraph graph = cast(CompoundDirectedGraph)g;
+    Iterator containment = graph.containment.iterator();
+    while (containment.hasNext())
+        graph.removeEdge(cast(Edge)containment.next());
+    graph.containment.clear();
+    (new LocalOptimizer())
+        .visit(graph);
+}
+
+private void pullTogetherSubgraphs() {
+    if (true)
+        return;
+    for (int j = 0; j < rank.count(); j++) {
+        Node n = rank.getNode(j);
+        Subgraph s = n.getParent();
+        while (s !is null) {
+            getRowEntry(s, currentRow).reset();
+            s = s.getParent();
+        }
+    }
+    for (int j = 0; j < rank.count(); j++) {
+        Node n = rank.getNode(j);
+        Subgraph s = n.getParent();
+        while (s !is null) {
+            RowEntry entry = getRowEntry(s, currentRow);
+            entry.count++;
+            entry.contribution += n.sortValue;
+            s = s.getParent();
+        }
+    }
+
+    double weight = 0.5;// * (1.0 - progress) * 3;
+
+    for (int j = 0; j < rank.count(); j++) {
+        Node n = rank.getNode(j);
+        Subgraph s = n.getParent();
+        if (s !is null) {
+            RowEntry entry = getRowEntry(s, currentRow);
+            n.sortValue =
+                n.sortValue * (1.0 - weight) + weight * entry.contribution / entry.count;
+        }
+    }
+}
+
+double evaluateNodeOutgoing() {
+    double result = super.evaluateNodeOutgoing();
+//  result += Math.random() * rankSize * (1.0 - progress) / 3.0;
+    if (progress > 0.2) {
+        Subgraph s = node.getParent();
+        double connectivity = mergeConnectivity(s, node.rank + 1, result, progress);
+        result = connectivity;
+    }
+    return result;
+}
+
+double evaluateNodeIncoming() {
+    double result = super.evaluateNodeIncoming();
+//  result += Math.random() * rankSize * (1.0 - progress) / 3.0;
+    if (progress > 0.2) {
+        Subgraph s = node.getParent();
+        double connectivity = mergeConnectivity(s, node.rank - 1, result, progress);
+        result = connectivity;
+    }
+    return result;
+}
+
+double mergeConnectivity(Subgraph s, int row, double result, double scaleFactor) {
+    while (s !is null && getRowEntry(s, row) is null)
+        s = s.getParent();
+    if (s !is null) {
+        RowEntry entry = getRowEntry(s, row);
+        double connectivity = entry.contribution / entry.count;
+        result = connectivity * 0.3 + (0.7) * result;
+        s = s.getParent();
+    }
+    return result;
+}
+
+RowEntry getRowEntry(Subgraph s, int row) {
+    key.s = s;
+    key.rank = row;
+    return cast(RowEntry)map.get(key);
+}
+
+void copyConstraints(NestingTree tree) {
+    if (tree.subgraph !is null)
+        tree.sortValue = tree.subgraph.rowOrder;
+    for (int i = 0; i < tree.contents.size(); i++) {
+        Object child = tree.contents.get(i);
+        if (auto n = cast(Node)child ) {
+            n.sortValue = n.rowOrder;
+        } else {
+            copyConstraints(cast(NestingTree)child);
+        }
+    }
+}
+
+public void init(DirectedGraph g) {
+    super.init(g);
+    init_ = true;
+
+    for (int row = 0; row < g.ranks.size(); row++) {
+        Rank rank = g.ranks.getRank(row);
+
+        NestingTree tree = NestingTree.buildNestingTreeForRank(rank);
+        copyConstraints(tree);
+        tree.recursiveSort(true);
+        rank.clear();
+        tree.repopulateRank(rank);
+
+        for (int j = 0; j < rank.count(); j++) {
+            Node n = rank.getNode(j);
+            Subgraph s = n.getParent();
+            while (s !is null) {
+                addRowEntry(s, row);
+                s = s.getParent();
+            }
+        }
+    }
+}
+
+protected void postSort() {
+    super.postSort();
+    if (init_)
+        updateRank(rank);
+}
+
+void updateRank(Rank rank) {
+    for (int j = 0; j < rank.count(); j++) {
+        Node n = rank.getNode(j);
+        Subgraph s = n.getParent();
+        while (s !is null) {
+            getRowEntry(s, currentRow).reset();
+            s = s.getParent();
+        }
+    }
+    for (int j = 0; j < rank.count(); j++) {
+        Node n = rank.getNode(j);
+        Subgraph s = n.getParent();
+        while (s !is null) {
+            RowEntry entry = getRowEntry(s, currentRow);
+            entry.count++;
+            entry.contribution += n.index;
+            s = s.getParent();
+        }
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/CompoundVerticalPlacement.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.CompoundVerticalPlacement;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.VerticalPlacement;
+import dwtx.draw2d.graph.CompoundDirectedGraph;
+import dwtx.draw2d.graph.Subgraph;
+
+/**
+ * calculates the height and y-coordinates for nodes and subgraphs in a compound directed
+ * graph.
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+class CompoundVerticalPlacement : VerticalPlacement {
+
+/**
+ * @see GraphVisitor#visit(DirectedGraph)
+ * Extended to set subgraph values.
+ */
+void visit(DirectedGraph dg) {
+    CompoundDirectedGraph g = cast(CompoundDirectedGraph)dg;
+    super.visit(g);
+    for (int i = 0; i < g.subgraphs.size(); i++) {
+        Subgraph s = cast(Subgraph)g.subgraphs.get(i);
+        s.y = s.head.y;
+        s.height = s.tail.height + s.tail.y - s.y;
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/ConvertCompoundGraph.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.ConvertCompoundGraph;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.graph.GraphVisitor;
+import dwtx.draw2d.graph.NodeList;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.Subgraph;
+import dwtx.draw2d.graph.SubgraphBoundary;
+import dwtx.draw2d.graph.CompoundDirectedGraph;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.Edge;
+
+/**
+ * Converts a compound directed graph into a simple directed graph.
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+class ConvertCompoundGraph : GraphVisitor {
+
+private void addContainmentEdges(CompoundDirectedGraph graph) {
+    //For all nested nodes, connect to head and/or tail of containing subgraph if present
+    for (int i = 0; i < graph.nodes.size(); i++) {
+        Node node = graph.nodes.getNode(i);
+        Subgraph parent = node.getParent();
+        if (parent is null)
+            continue;
+        if (auto sub = cast(Subgraph)node ) {
+            connectHead(graph, sub.head, parent);
+            connectTail(graph, sub.tail, parent);
+        } else {
+            connectHead(graph, node, parent);
+            connectTail(graph, node, parent);
+        }
+    }
+}
+
+int buildNestingTreeIndices(NodeList nodes, int base) {
+    for (int i = 0; i < nodes.size(); i++) {
+        Node node = cast(Node)nodes.get(i);
+        if (auto s = cast(Subgraph)node ) {
+            s.nestingTreeMin = base;
+            base = buildNestingTreeIndices(s.members, base);
+        }
+        node.nestingIndex = base++;
+    }
+    return base++;
+}
+
+private void connectHead(CompoundDirectedGraph graph, Node node, Subgraph parent) {
+    bool connectHead = true;
+    for (int j = 0; connectHead && j < node.incoming.size(); j++) {
+        Node ancestor = node.incoming.getEdge(j).source;
+        if (parent.isNested(ancestor))
+            connectHead = false;
+    }
+    if (connectHead) {
+        Edge e = new Edge(parent.head, node);
+        e.weight = 0;
+        graph.edges.add(e);
+        graph.containment.add(e);
+    }
+}
+
+private void connectTail(CompoundDirectedGraph graph, Node node, Subgraph parent) {
+    bool connectTail = true;
+    for (int j = 0; connectTail && j < node.outgoing.size(); j++) {
+        Node ancestor = node.outgoing.getEdge(j).target;
+        if (parent.isNested(ancestor))
+            connectTail = false;
+    }
+    if (connectTail) {
+        Edge e = new Edge(node, parent.tail);
+        e.weight = 0;
+        graph.edges.add(e);
+        graph.containment.add(e);
+    }
+}
+
+private void convertSubgraphEndpoints(CompoundDirectedGraph graph) {
+    for (int i = 0; i < graph.edges.size(); i++) {
+        Edge edge = cast(Edge)graph.edges.get(i);
+        if (auto s = cast(Subgraph)edge.source ) {
+            Node newSource;
+            if (s.isNested(edge.target))
+                newSource = s.head;
+            else
+                newSource = s.tail;
+            //s.outgoing.remove(edge);
+            edge.source = newSource;
+            newSource.outgoing.add(edge);
+        }
+        if (auto s = cast(Subgraph)edge.target ) {
+            Node newTarget;
+            if (s.isNested(edge.source))
+                newTarget = s.tail;
+            else
+                newTarget = s.head;
+
+            //s.incoming.remove(edge);
+            edge.target = newTarget;
+            newTarget.incoming.add(edge);
+        }
+    }
+}
+
+private void replaceSubgraphsWithBoundaries(CompoundDirectedGraph graph) {
+    for (int i = 0; i < graph.subgraphs.size(); i++) {
+        Subgraph s = cast(Subgraph)graph.subgraphs.get(i);
+        graph.nodes.add(s.head);
+        graph.nodes.add(s.tail);
+        graph.nodes.remove(s);
+    }
+}
+
+void revisit(DirectedGraph g) {
+    for (int i = 0; i < g.edges.size(); i++) {
+        Edge e = g.edges.getEdge(i);
+        if (null !is cast(SubgraphBoundary)e.source ) {
+            e.source.outgoing.remove(e);
+            e.source = e.source.getParent();
+        }
+        if (null !is cast(SubgraphBoundary)e.target ) {
+            e.target.incoming.remove(e);
+            e.target = e.target.getParent();
+        }
+    }
+}
+
+/**
+ * @see GraphVisitor#visit(dwtx.draw2d.graph.DirectedGraph)
+ */
+public void visit(DirectedGraph dg) {
+    CompoundDirectedGraph graph = cast(CompoundDirectedGraph)dg;
+
+    NodeList roots = new NodeList();
+    //Find all subgraphs and root subgraphs
+    for (int i = 0; i < graph.nodes.size(); i++) {
+        Object node = graph.nodes.get(i);
+        if (auto s = cast(Subgraph)node ) {
+            Insets padding = dg.getPadding(s);
+            s.head = new SubgraphBoundary(s, padding, 0);
+            s.tail = new SubgraphBoundary(s, padding, 2);
+            Edge headToTail = new Edge(s.head, s.tail);
+            headToTail.weight = 10;
+            graph.edges.add(headToTail);
+            graph.containment.add(headToTail);
+
+            graph.subgraphs.add(s);
+            if (s.getParent() is null)
+                roots.add(s);
+            if (s.members.size() is 2) //The 2 being the head and tail only
+                graph.edges.add(new Edge(s.head, s.tail));
+        }
+    }
+
+    buildNestingTreeIndices(roots, 0);
+    convertSubgraphEndpoints(graph);
+    addContainmentEdges(graph);
+    replaceSubgraphsWithBoundaries(graph);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/DirectedGraph.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.DirectedGraph;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.PositionConstants;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.graph.EdgeList;
+import dwtx.draw2d.graph.NodeList;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.RankList;
+import dwtx.draw2d.graph.Edge;
+import dwtx.draw2d.graph.Rank;
+
+/**
+ * A graph consisting of nodes and directed edges.  A DirectedGraph serves as the input to
+ * a graph layout algorithm.  The algorithm will place the graph's nodes and edges
+ * according to certain goals, such as short, non-crossing edges, and readability.
+ *
+ * @author hudsonr
+ * @since 2.1.2
+ */
+public class DirectedGraph {
+
+private int direction = PositionConstants.SOUTH;
+
+/**
+ * The default padding to be used for nodes which don't specify any padding.  Padding is
+ * the amount of empty space to be left around a node.  The default value is undefined.
+ */
+private Insets defaultPadding;
+
+/**
+ * All of the edges in the graph.
+ */
+public EdgeList edges;
+
+/**
+ * All of the nodes in the graph.
+ */
+public NodeList nodes;
+
+/**
+ * For internal use only. The list of rows which makeup the final graph layout.
+ * @deprecated
+ */
+public RankList ranks;
+
+Node forestRoot;
+Insets margin;
+int[] rankLocations;
+int[][] cellLocations;
+int tensorStrength;
+int tensorSize;
+Dimension size;
+
+public this(){
+    defaultPadding = new Insets(16);
+    edges = new EdgeList();
+    nodes = new NodeList();
+    ranks = new RankList();
+    margin = new Insets();
+    size = new Dimension();
+}
+
+/**
+ * Returns the default padding for nodes.
+ * @return the default padding
+ * @since 3.2
+ */
+public Insets getDefaultPadding() {
+    return defaultPadding;
+}
+
+/**
+ * Returns the direction in which the graph will be layed out.
+ * @return the layout direction
+ * @since 3.2
+ */
+public int getDirection() {
+    return direction;
+}
+
+/**
+ * Sets the outer margin for the entire graph. The margin is the space in which nodes
+ * should not be placed.
+ * @return the graph's margin
+ * @since 3.2
+ */
+public Insets getMargin() {
+    return margin;
+}
+
+/**
+ * Returns the effective padding for the given node.  If the node has a specified padding,
+ * it will be used, otherwise, the graph's defaultPadding is returned.  The
+ * returned value must not be modified.
+ * @param node the node
+ * @return the effective padding for that node
+ */
+public Insets getPadding(Node node) {
+    Insets pad = node.getPadding();
+    if (pad is null)
+        return defaultPadding;
+    return pad;
+}
+
+int[] getCellLocations(int rank) {
+    return cellLocations[rank];
+}
+
+int[] getRankLocations() {
+    return rankLocations;
+}
+//
+//public Cell getCell(Point pt) {
+//  int rank = 0;
+//  while (rank < rankLocations.length - 1 && rankLocations[rank] < pt.y)
+//      rank++;
+//  int cells[] = cellLocations[rank];
+//  int cell = 0;
+//  while (cell < cells.length - 1 && cells[cell] < pt.x)
+//      cell++;
+//  return new Cell(rank, cell, ranks.getRank(rank).getNode(index));
+//}
+
+public Node getNode(int rank, int index) {
+    if (ranks.size() <= rank)
+        return null;
+    Rank r = ranks.getRank(rank);
+    if (r.size() <= index)
+        return null;
+    return r.getNode(index);
+}
+
+/**
+ * Removes the given edge from the graph.
+ * @param edge the edge to be removed
+ */
+public void removeEdge(Edge edge) {
+    edges.remove(edge);
+    edge.source.outgoing.remove(edge);
+    edge.target.incoming.remove(edge);
+    if (edge.vNodes !is null)
+        for (int j = 0; j < edge.vNodes.size(); j++)
+            removeNode(edge.vNodes.getNode(j));
+}
+
+/**
+ * Removes the given node from the graph. Does not remove the node's edges.
+ * @param node the node to remove
+ */
+public void removeNode(Node node) {
+    nodes.remove(node);
+    if (ranks !is null)
+        ranks.getRank(node.rank).remove(node);
+}
+
+/**
+ * Sets the default padding for all nodes in the graph.  Padding is the empty space left
+ * around the <em>outside</em> of each node.  The default padding is used for all nodes
+ * which do not specify a specific amount of padding (i.e., their padding is
+ * <code>null</code>).
+ * @param insets the padding
+ */
+public void setDefaultPadding(Insets insets) {
+    defaultPadding = insets;
+}
+
+/**
+ * Sets the layout direction for the graph. Edges will be layed out in the specified
+ * direction (unless the graph contains cycles). Supported values are:
+ * <UL>
+ * <LI>{@link dwtx.draw2d.PositionConstants#EAST}
+ * <LI>{@link dwtx.draw2d.PositionConstants#SOUTH}
+ * </UL>
+ * <P>The default direction is south.
+ * @param direction the layout direction
+ * @since 3.2
+ */
+public void setDirection(int direction) {
+    this.direction = direction;
+}
+
+//public void setGraphTensor(int length, int strength) {
+//  tensorStrength = strength;
+//  tensorSize = length;
+//}
+
+/**
+ * Sets the graphs margin.
+ * @param insets the graph's margin
+ * @since 3.2
+ */
+public void setMargin(Insets insets) {
+    this.margin = insets;
+}
+
+public Dimension getLayoutSize() {
+    return size;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/DirectedGraphLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.DirectedGraphLayout;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.draw2d.graph.TransposeMetrics;
+import dwtx.draw2d.graph.BreakCycles;
+import dwtx.draw2d.graph.RouteEdges;
+import dwtx.draw2d.graph.InitialRankSolver;
+import dwtx.draw2d.graph.TightSpanningTreeSolver;
+import dwtx.draw2d.graph.RankAssignmentSolver;
+import dwtx.draw2d.graph.PopulateRanks;
+import dwtx.draw2d.graph.VerticalPlacement;
+import dwtx.draw2d.graph.MinCross;
+import dwtx.draw2d.graph.LocalOptimizer;
+import dwtx.draw2d.graph.HorizontalPlacement;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.GraphVisitor;
+
+/**
+ * Performs a graph layout of a <code>DirectedGraph</code>.  The directed graph must meet
+ * the following conditions:
+ * <UL>
+ *   <LI>The graph must be connected.
+ *   <LI>All edge's must be added to the graph's {@link DirectedGraph#edges edges} list
+ *   exactly once.
+ *   <LI>All nodes must be added to the graph's {@link DirectedGraph#nodes nodes} list
+ *   exactly once.
+ * </UL>
+ *
+ * This algorithm will:
+ * <UL>
+ *   <LI>break cycles by inverting a set of feedback edges. Feedback edges will have the
+ *   flag {@link Edge#isFeedback} set to <code>true</code>.  The following statements are
+ *   true with respect to the inverted edge. When the algorithm completes, it will invert
+ *   the edges again, but will leave the feedback flags set.
+ *   <LI>for each node <em>n</em>, assign n to a "rank" R(n), such that: for each edge (m,
+ *   n) in n.incoming, R(m)<=R(n)-(m,n).delta.   The total weighted edge lengths shall be
+ *   no greater than is necessary to meet this requirement for all edges in the graph.
+ *   <LI>attempt to order the nodes in their ranks as to minimize crossings.
+ *   <LI>assign <em>y</em> coordinates to each node based on its rank.  The spacing
+ *   between ranks is the sum of the bottom padding of the previous rank, and the top
+ *   padding of the next rank.
+ *   <LI>assign <em>x</em> coordinates such that the graph is easily readable.  The exact
+ *   behavior is undefined, but will favor edge's with higher {@link Edge#weight}s.  The
+ *   minimum x value assigned to a node or bendpoint will be 0.
+ *   <LI>assign <em>bendpoints</em> to all edge's which span more than 1 rank.
+ * </UL>
+ * <P>For each NODE:
+ * <UL>
+ *   <LI>The x coordinate will be assigned a value >= 0
+ *   <LI>The y coordinate will be assigned a value >= 0
+ *   <LI>The rank will be assigned a value >=0
+ *   <LI>The height will be set to the height of the tallest node on the same row
+ * </UL>
+ * <P>For each EDGE:
+ * <UL>
+ *   <LI>If an edge spans more than 1 row, it will have a list of   {@link
+ * dwtx.draw2d.graph.Edge#vNodes virtual} nodes.  The virtual nodes will   be
+ * assigned an x coordinate indicating the routing path for that edge.
+ *   <LI>If an edge is a feedback edge, it's <code>isFeedback</code> flag will be set,
+ * and if it has virtual nodes, they will be in reverse order (bottom-up).
+ * </UL>
+ * <P>This class is not guaranteed to produce the same results for each invocation.
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+public class DirectedGraphLayout {
+
+List steps;
+
+/**
+ * @since 3.1
+ */
+public this() {
+    steps = new ArrayList();
+    init();
+}
+
+void init() {
+    steps.add(new TransposeMetrics());
+    steps.add(new BreakCycles());
+    steps.add(new RouteEdges());
+    steps.add(new InitialRankSolver());
+    steps.add(new TightSpanningTreeSolver());
+    steps.add(new RankAssignmentSolver());
+    steps.add(new PopulateRanks());
+    steps.add(new VerticalPlacement());
+    steps.add(new MinCross());
+    steps.add(new LocalOptimizer());
+    steps.add(new HorizontalPlacement());
+}
+
+/**
+ * Lays out the given graph
+ * @param graph the graph to layout
+ */
+public void visit(DirectedGraph graph) {
+    if (graph.nodes.isEmpty())
+        return;
+    for (int i = 0; i < steps.size(); i++) {
+        GraphVisitor visitor = cast(GraphVisitor)steps.get(i);
+        visitor.visit(graph);
+    }
+    for (int i = steps.size() - 1; i >= 0; i--) {
+        GraphVisitor visitor = cast(GraphVisitor)steps.get(i);
+        visitor.revisit(graph);
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/Edge.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,386 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.Edge;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.graph.NodeList;
+import dwtx.draw2d.graph.Node;
+
+/**
+ * A directed Edge joining a source and target Node.  Edges indicate the dependencies
+ * between nodes.  An Edge provides the information needed to perform a graph layout, and
+ * it stores the result of the layout in its various field. Therefore, it functions
+ * both as input and output.  The input data consists of:
+ * <UL>
+ *   <LI>{@link #source} - the source Node
+ *   <LI>{@link #target} - the target Node
+ *   <LI>{@link #delta} - the minimum number of rows the edge should span
+ *   <LI>{@link #weight} - a hint indicating this edge's importance
+ *   <LI>{@link #width} - the edge's width
+ *   <LI>{@link #padding} - the amount of space to leave on either side of the edge
+ *   <LI>[{@link #offsetSource}] - the edge's attachment point at the source node
+ *   <LI>[{@link #offsetTarget}] - the edge's attachment point at the target node
+ * </UL>
+ * <P>The output of a layout consists of bending longer edges, and potentially inverting
+ * edges to remove cycles in the graph.  The output consists of:
+ * <UL>
+ *   <LI>{@link #vNodes} - the virtual nodes (if any) which make up the bendpoints
+ *   <LI>{@link #isFeedback} - <code>true</code> if the edge points backwards
+ * </UL>
+ *
+ * @author hudsonr
+ * @since 2.1.2
+ */
+public class Edge {
+
+int cut;
+
+/**
+ * An arbitrary data field for use by clients.
+ */
+public Object data;
+
+/**
+ * The minimum rank separation between the source and target nodes. The default value is
+ * 1.
+ * @deprecated use accessors instead
+ */
+public int delta = 1;
+
+/**
+ * The ending point.
+ * @deprecated use {@link #getPoints()}
+ */
+public Point end;
+
+bool flag;
+
+/**
+ * @deprecated INTERNAL field, use accessor method
+ * Indicates an edge was inverted during the layout
+ */
+public bool isFeedback_ = false;
+
+/**
+ * The edge's attachment point at the <em>source</em> node. The default value is -1, which
+ * indicates that the edge should use the node's default {@link Node#getOffsetOutgoing()
+ * outgoing} attachment point.
+ * @deprecated use accessors instead
+ */
+public int offsetSource = -1;
+
+/**
+ * The edge's attachment point at the <em>target</em> node. The default value is -1, which
+ * indicates that the edge should use the node's default {@link Node#getOffsetIncoming()
+ * incoming} attachment point.
+ * @deprecated use accessors instead
+ */
+public int offsetTarget = -1;
+
+/**
+ * The minimum amount of space to leave on both the left and right sides of the edge.
+ * @deprecated use accessors instead
+ */
+public int padding = 10;
+
+private PointList points;
+
+/**
+ * The source Node.
+ */
+public Node source;
+/**
+ * The starting point.
+ * @deprecated use {@link #getPoints()}
+ */
+public Point start;
+
+/**
+ * The target Node.
+ */
+public Node target;
+
+bool tree;
+
+/**
+ * The virtual nodes used to bend edges which go across one or more ranks.  Each virtual
+ * node is just a regular node which occupies some small amount of space on a row. It's
+ * width is equivalent to the edge's width.  Clients can use each virtual node's location
+ * (x, y, width, and height) as the way to position an edge which spans multiple rows.
+ */
+public NodeList vNodes;
+/**
+ * A hint indicating how straight and short the edge should be relative to
+ * other edges in the graph. The default value is <code>1</code>.
+ */
+public int weight = 1;
+
+/**
+ * @deprecated use accessors instead
+ */
+public int width = 1;
+
+/**
+ * Constructs a new edge with the given source and target nodes.  All other fields will
+ * have their default values.
+ * @param source the source Node
+ * @param target the target Node
+ */
+public this(Node source, Node target) {
+    this(null, source, target);
+}
+
+/**
+ * Constructs a new edge with the given source, target, delta, and weight.
+ * @param source the source Node
+ * @param target the target Node
+ * @param delta the minimum edge span
+ * @param weight the weight hint
+ */
+public this(Node source, Node target, int delta, int weight) {
+    this(source, target);
+    this.delta = delta;
+    this.weight = weight;
+}
+
+/**
+ * Constructs a new edge with the given data object, source, and target node.
+ * @param data an arbitrary data object
+ * @param source the source node
+ * @param target the target node
+ */
+public this(Object data, Node source, Node target) {
+    this.data = data;
+    this.source = source;
+    this.target = target;
+    source.outgoing.add(this);
+    target.incoming.add(this);
+}
+
+/**
+ * Returns the delta value. The delta is the minimum rank separation for the edge's source
+ * and target nodes.
+ * @return the delta.
+ * @since 3.2
+ */
+public int getDelta() {
+    return delta;
+}
+
+/**
+ * For internal use only. Returns the index of the {@link Node} (or {@link VirtualNode})
+ * on this edge at the given rank.  If this edge doesn't have a node at the given rank, -1
+ * is returned.
+ * @param rank the rank
+ * @return the edges index at the given rank
+ */
+int getIndexForRank(int rank) {
+    if (source.rank is rank)
+        return source.index;
+    if (target.rank is rank)
+        return target.index;
+    if (vNodes !is null)
+        return vNodes.getNode(rank - source.rank - 1).index;
+    return -1;
+}
+
+/**
+ * For internal use only. Returns the target node's row minus the source node's row.
+ * @return the distance from the source to target ranks
+ */
+public int getLength() {
+    return (target.rank - source.rank);
+}
+
+public int getPadding() {
+    return padding;
+}
+
+/**
+ * Returns the path connecting the edge's source and target.
+ * @return a point list
+ * @since 3.2
+ */
+public PointList getPoints() {
+    return points;
+}
+
+int getSlack() {
+    return (target.rank - source.rank) - delta;
+}
+
+/**
+ * Returns the effective source offset for this edge.  The effective source offset is
+ * either the {@link #offsetSource} field, or the source node's default outgoing offset if
+ * that field's value is -1.
+ * @return the source offset
+ */
+public int getSourceOffset() {
+    if (offsetSource !is -1)
+        return offsetSource;
+    return source.getOffsetOutgoing();
+}
+
+/**
+ * Returns the effective target offset for this edge.  The effective target offset is
+ * either the {@link #offsetTarget} field, or the target node's default incoming offset if
+ * that field's value is -1.
+ * @return the target offset
+ */
+public int getTargetOffset() {
+    if (offsetTarget !is -1)
+        return offsetTarget;
+    return target.getOffsetIncoming();
+}
+
+public int getWidth() {
+    return width;
+}
+
+/**
+ * Swaps the source and target nodes. If any positional data has been calculated, it is
+ * inverted as well to reflect the new direction.
+ * @since 2.1.2
+ */
+public void invert() {
+    source.outgoing.remove(this);
+    target.incoming.remove(this);
+
+    Node oldTarget = target;
+    target = source;
+    source = oldTarget;
+
+    int temp = offsetSource;
+    offsetSource = offsetTarget;
+    offsetTarget = temp;
+
+    target.incoming.add(this);
+    source.outgoing.add(this);
+
+    if (points !is null)
+        points.reverse();
+
+    if (vNodes !is null) {
+        NodeList newVNodes = new NodeList();
+        for (int j = vNodes.size() - 1; j >= 0; j--) {
+            newVNodes.add(vNodes.getNode(j));
+        }
+        vNodes = newVNodes;
+    }
+
+    if (start !is null) {
+        Point pt = start;
+        start = end;
+        end = pt;
+    }
+}
+
+/**
+ * Returns <code>true</code> if the edge was a feedback edge. The layout algorithm may
+ * invert one or more edges to remove all cycles from the input. The set of edges that are
+ * inverted are referred to as the "feedback" set.
+ * @return <code>true</code> if the edge is feedback
+ * @since 3.2
+ */
+public bool isFeedback() {
+    return isFeedback_;
+}
+
+/**
+ * For internal use only.  Returns the node opposite the given node on this edge.
+ * @param end one end
+ * @return the other end
+ */
+public Node opposite(Node end) {
+    if (source is end)
+        return target;
+    return source;
+}
+
+/**
+ * Sets the delta value.
+ * @param delta the new delta value
+ * @since 3.2
+ */
+public void setDelta(int delta) {
+    this.delta = delta;
+}
+
+/**
+ * Sets the padding for this edge.
+ * @param padding the padding
+ * @since 3.2
+ */
+public void setPadding(int padding) {
+    this.padding = padding;
+}
+
+void setPoints(PointList points) {
+    this.points = points;
+    start = points.getFirstPoint();
+    end = points.getLastPoint();
+}
+
+/**
+ * Sets the source node and adds this edge to the new source's outgoing edges. If the
+ * source node is previously set, removes this edge from the old source's outgoing edges.
+ * @param node the new source
+ * @since 3.2
+ */
+public void setSource(Node node) {
+    if (source is node)
+        return;
+    if (source !is null)
+        source.outgoing.remove(this);
+    source = node;
+    if (source !is null)
+        source.outgoing.add(this);
+}
+
+public void setSourceOffset(int offset) {
+    this.offsetSource = offset;
+}
+
+/**
+ * Sets the target node and adds this edge to the new target's incoming edges. If the
+ * target node is previously set, removes this edge from the old target's incoming edges.
+ * @param node the new target
+ * @since 3.2
+ */
+public void setTarget(Node node) {
+    if (target is node)
+        return;
+    if (target !is null)
+        target.incoming.remove(this);
+    target = node;
+    if (target !is null)
+        target.incoming.add(this);
+}
+
+public void setTargetOffset(int offset) {
+    this.offsetTarget = offset;
+}
+
+/**
+ * Sets the width of the edge.
+ * @param width the new width
+ * @since 3.2
+ */
+public void setWidth(int width) {
+    this.width = width;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/EdgeList.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.EdgeList;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.draw2d.graph.Edge;
+/**
+ * A list of <code>Edge</code>s.
+ * @author hudsonr
+ * @since 2.1.2
+ */
+public class EdgeList : ArrayList {
+
+/**
+ * Returns the edge for the given index.
+ * @param index the index of the requested edge
+ * @return the edge at the given index
+ */
+public Edge getEdge(int index) {
+    return cast(Edge)super.get(index);
+}
+
+/**
+ * For intrenal use only.
+ * @param i and index
+ * @return a value
+ */
+public int getSourceIndex(int i) {
+    return getEdge(i).source.index;
+}
+
+/**
+ * For internal use only.
+ * @param i an index
+ * @return a value
+ */
+public int getTargetIndex(int i) {
+    return getEdge(i).target.index;
+}
+
+/**
+ * For internal use only.
+ * @return the minimum slack for this edge list
+ */
+public int getSlack() {
+    int slack = Integer.MAX_VALUE;
+    for (int i = 0; i < this.size(); i++)
+        slack = Math.min(slack, getEdge(i).getSlack());
+    return slack;
+}
+
+/**
+ * For internal use only.
+ * @return the total weight of all edges
+ */
+public int getWeight() {
+    int w = 0;
+    for (int i = 0; i < this.size(); i++)
+        w += getEdge(i).weight;
+    return w;
+}
+
+/**
+ * For internal use only
+ * @return <code>true</code> if completely flagged
+ */
+public bool isCompletelyFlagged() {
+    for (int i = 0; i < size(); i++) {
+        if (!getEdge(i).flag)
+            return false;
+    }
+    return true;
+}
+
+/**
+ * For internal use only.  Resets all flags.
+ * @param resetTree internal
+ */
+public void resetFlags(bool resetTree) {
+    for (int i = 0; i < size(); i++) {
+        getEdge(i).flag = false;
+        if (resetTree)
+            getEdge(i).tree = false;
+    }
+}
+
+/**
+ * For internal use only.
+ * @param value value
+ */
+public void setFlags(bool value) {
+    for (int i = 0; i < size(); i++)
+        getEdge(i).flag = value;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/GraphUtilities.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.GraphUtilities;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.graph.Subgraph;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.NodeList;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.Rank;
+import dwtx.draw2d.graph.Edge;
+import dwtx.draw2d.graph.EdgeList;
+
+/**
+ * Some utility methods for graphs.
+ * @author Eric Bordeau
+ * @since 2.1.2
+ */
+class GraphUtilities {
+
+static Subgraph getCommonAncestor(Node left, Node right) {
+    Subgraph parent;
+    if (auto p = cast(Subgraph)right )
+        parent = p;
+    else
+        parent = right.getParent();
+    while (parent !is null) {
+        if (parent.isNested(left))
+            return parent;
+        parent = parent.getParent();
+    }
+    return null;
+}
+
+/**
+ * Returns <code>true</code> if the given graph contains at least one cycle.
+ * @param graph the graph to test
+ * @return whether the graph is cyclic
+ */
+public static bool isCyclic(DirectedGraph graph) {
+    return isCyclic(new NodeList(graph.nodes));
+}
+
+/**
+ * Recursively removes leaf nodes from the list until there are no nodes remaining (acyclic)
+ * or there are no leaf nodes but the list is not empty (cyclic), then returns the result.
+ * @param nodes the list of nodes to test
+ * @return whether the graph is cyclic
+ */
+public static bool isCyclic(NodeList nodes) {
+    if (nodes.isEmpty())
+        return false;
+    int size = nodes.size();
+    // remove all the leaf nodes from the graph
+    for (int i = 0; i < nodes.size(); i++) {
+        Node node = nodes.getNode(i);
+        if (node.outgoing is null || node.outgoing.isEmpty()) { // this is a leaf node
+            nodes.remove(node);
+            for (int j = 0; j < node.incoming.size(); j++) {
+                Edge e = node.incoming.getEdge(j);
+                e.source.outgoing.remove(e);
+            }
+        }
+    }
+    //  if no nodes were removed, that means there are no leaf nodes and the graph is cyclic
+    if (nodes.size() is size)
+        return true;
+    // leaf nodes were removed, so recursively call this method with the new list
+    return isCyclic(nodes);
+}
+
+/**
+ * Counts the number of edge crossings in a DirectedGraph
+ * @param graph the graph whose crossed edges are counted
+ * @return the number of edge crossings in the graph
+ */
+public static int numberOfCrossingsInGraph(DirectedGraph graph) {
+    int crossings = 0;
+    for (int i = 0; i < graph.ranks.size(); i++) {
+        Rank rank = graph.ranks.getRank(i);
+        crossings += numberOfCrossingsInRank(rank);
+    }
+    return crossings;
+}
+
+/**
+ * Counts the number of edge crossings in a Rank
+ * @param rank the rank whose crossed edges are counted
+ * @return the number of edge crossings in the rank
+ */
+public static int numberOfCrossingsInRank(Rank rank) {
+    int crossings = 0;
+    for (int i = 0; i < rank.size() - 1; i++) {
+        Node currentNode = rank.getNode(i);
+        Node nextNode;
+        for (int j = i + 1; j < rank.size(); j++) {
+            nextNode = rank.getNode(j);
+            EdgeList currentOutgoing = currentNode.outgoing;
+            EdgeList nextOutgoing = nextNode.outgoing;
+            for (int k = 0; k < currentOutgoing.size(); k++) {
+                Edge currentEdge = currentOutgoing.getEdge(k);
+                for (int l = 0; l < nextOutgoing.size(); l++) {
+                    if (nextOutgoing.getEdge(l).getIndexForRank(currentNode.rank + 1)
+                        < currentEdge.getIndexForRank(currentNode.rank + 1))
+                        crossings++;
+                }
+            }
+        }
+    }
+    return crossings;
+}
+
+private static NodeList search(Node node, NodeList list) {
+    if (node.flag)
+        return list;
+    node.flag = true;
+    list.add(node);
+    for (int i = 0; i < node.outgoing.size(); i++)
+        search(node.outgoing.getEdge(i).target, list);
+    return list;
+}
+
+/**
+ * Returns <code>true</code> if adding an edge between the 2 given nodes will introduce a
+ * cycle in the containing graph.
+ * @param source the potential source node
+ * @param target the potential target node
+ * @return whether an edge between the 2 given nodes will introduce a cycle
+ */
+public static bool willCauseCycle(Node source, Node target) {
+    NodeList nodes = search(target, new NodeList());
+    nodes.resetFlags();
+    return nodes.contains(source);
+}
+
+static bool isConstrained(Node left, Node right) {
+    Subgraph common = left.getParent();
+    while (common !is null && !common.isNested(right)) {
+        left = left.getParent();
+        common = left.getParent();
+    }
+    while (right.getParent() !is common)
+        right = right.getParent();
+    return (left.rowOrder !is -1 && right.rowOrder !is -1)
+      && left.rowOrder !is right.rowOrder;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/GraphVisitor.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.GraphVisitor;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.graph.DirectedGraph;
+
+/**
+ * Performs some action on a Graph.
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+abstract class GraphVisitor {
+
+/**
+ * Act on the given directed graph.
+ * @param g the graph
+ */
+void visit(DirectedGraph g) { }
+
+/**
+ * Called in reverse order of visit.
+ * @since 3.1
+ * @param g the graph to act upon
+ */
+void revisit(DirectedGraph g) { }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/HorizontalPlacement.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,428 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.HorizontalPlacement;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.draw2d.graph.SpanningTreeVisitor;
+import dwtx.draw2d.graph.NodeCluster;
+import dwtx.draw2d.graph.NodePair;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.Edge;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.RankList;
+import dwtx.draw2d.graph.CollapsedEdges;
+import dwtx.draw2d.graph.Rank;
+import dwtx.draw2d.graph.EdgeList;
+import dwtx.draw2d.graph.InitialRankSolver;
+import dwtx.draw2d.graph.TightSpanningTreeSolver;
+import dwtx.draw2d.graph.RankAssignmentSolver;
+
+/**
+ * Assigns the X and width values for nodes in a directed graph.
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+class HorizontalPlacement : SpanningTreeVisitor {
+
+class ClusterSet {
+    int freedom = Integer.MAX_VALUE;
+    bool isRight;
+    public List members;
+    int pullWeight = 0;
+    int rawPull = 0;
+
+    public this(){
+        members = new ArrayList();
+    }
+
+    bool addCluster (NodeCluster seed) {
+        members.add(seed);
+        seed.isSetMember = true;
+
+        rawPull += seed.weightedTotal;
+        pullWeight += seed.weightedDivisor;
+        if (isRight) {
+            freedom = Math.min(freedom, seed.rightNonzero);
+            if (freedom is 0 || rawPull <= 0)
+                return true;
+            addIncomingClusters(seed);
+            if (addOutgoingClusters(seed))
+                return true;
+        } else {
+            freedom = Math.min(freedom, seed.leftNonzero);
+            if (freedom is 0 || rawPull >= 0)
+                return true;
+            addOutgoingClusters(seed);
+            if (addIncomingClusters(seed))
+                return true;
+        }
+        return false;
+    }
+
+    bool addIncomingClusters(NodeCluster seed) {
+        for (int i = 0; i < seed.leftCount; i++) {
+            NodeCluster neighbor = seed.leftNeighbors[i];
+            if (neighbor.isSetMember)
+                continue;
+            CollapsedEdges edges = seed.leftLinks[i];
+            if (!edges.isTight())
+                continue;
+            if ((!isRight || neighbor.getPull() > 0) && addCluster (neighbor))
+                return true;
+        }
+        return false;
+    }
+
+    bool addOutgoingClusters(NodeCluster seed) {
+        for (int i = 0; i < seed.rightCount; i++) {
+            NodeCluster neighbor = seed.rightNeighbors[i];
+            if (neighbor.isSetMember)
+                continue;
+            CollapsedEdges edges = seed.rightLinks[i];
+            if (!edges.isTight())
+                continue;
+            if ((isRight || neighbor.getPull() < 0) && addCluster (neighbor))
+                    return true;
+        }
+        return false;
+    }
+
+    bool build(NodeCluster seed) {
+        isRight = seed.weightedTotal > 0;
+        if (!addCluster(seed)) {
+            int delta = rawPull / pullWeight;
+            if (delta < 0)
+                delta = Math.max(delta, -freedom);
+            else
+                delta = Math.min(delta, freedom);
+            if (delta !is 0) {
+                for (int i = 0; i < members.size(); i++) {
+                    NodeCluster c = cast(NodeCluster)members.get(i);
+                    c.adjustRank(delta, dirtyClusters);
+                }
+                refreshDirtyClusters();
+                reset();
+                return true;
+            }
+        }
+        reset();
+        return false;
+    }
+
+    void reset() {
+        rawPull = pullWeight = 0;
+        for (int i = 0; i < members.size(); i++)
+            (cast(NodeCluster)members.get(i)).isSetMember = false;
+        members.clear();
+        freedom = Integer.MAX_VALUE;
+    }
+}
+
+static int step;
+private List allClusters;
+private Map clusterMap;
+ClusterSet clusterset;
+Collection dirtyClusters;
+DirectedGraph graph;
+Map map_;
+DirectedGraph prime;
+Node graphRight;
+Node graphLeft;
+
+public this(){
+    clusterMap = new HashMap();
+    clusterset = new ClusterSet();
+    dirtyClusters = new HashSet();
+    map_ = new HashMap();
+}
+
+
+/**
+ * Inset the corresponding parts for the given 2 nodes along an edge E.  The weight value
+ * is a value by which to scale the edges specified weighting factor.
+ * @param u the source
+ * @param v the target
+ * @param e the edge along which u and v exist
+ * @param weight a scaling for the weight
+ */
+void addEdge(Node u, Node v, Edge e, int weight) {
+    Node ne = new Node(new NodePair(u, v));
+    prime.nodes.add(ne);
+
+    ne.y = (u.y + u.height + v.y) / 2;
+    Node uPrime = get(u);
+    Node vPrime = get(v);
+
+    int uOffset = e.getSourceOffset();
+
+    int vOffset = e.getTargetOffset();
+
+    Edge eu = new Edge(ne, uPrime, 0, e.weight * weight);
+    Edge ev = new Edge(ne, vPrime, 0, e.weight * weight);
+
+    int dw = uOffset - vOffset;
+    if (dw < 0)
+        eu.delta = -dw;
+    else
+        ev.delta = dw;
+
+    prime.edges.add(eu);
+    prime.edges.add(ev);
+}
+
+/**
+ * Adds all of the incoming edges to the graph.
+ * @param n the original node
+ * @param nPrime its corresponding node in the auxilary graph
+ */
+void addEdges(Node n) {
+    for (int i = 0; i < n.incoming.size(); i++) {
+        Edge e = n.incoming.getEdge(i);
+        addEdge(e.source, n, e, 1);
+    }
+}
+
+void applyGPrime() {
+    Node node;
+    for (int n = 0; n < prime.nodes.size(); n++) {
+        node = prime.nodes.getNode(n);
+        if (null !is cast(Node)node.data )
+            (cast(Node)node.data).x = node.rank;
+    }
+}
+
+private void balanceClusters() {
+    findAllClusters();
+
+    step = 0;
+    bool somethingMoved = false;
+
+    for (int i = 0; i < allClusters.size();) {
+
+        NodeCluster c = cast(NodeCluster)allClusters.get(i);
+        int delta = c.getPull();
+        if (delta < 0) {
+            if (c.leftFreedom > 0) {
+                c.adjustRank(Math.max(delta, -c.leftFreedom), dirtyClusters);
+                refreshDirtyClusters();
+                moveClusterForward(i, c);
+                somethingMoved = true;
+                step++;
+            } else if (clusterset.build(c)) {
+                step++;
+                moveClusterForward(i, c);
+                somethingMoved = true;
+            }
+        } else if (delta > 0) {
+            if (c.rightFreedom > 0) {
+                c.adjustRank(Math.min(delta, c.rightFreedom), dirtyClusters);
+                refreshDirtyClusters();
+                moveClusterForward(i, c);
+                somethingMoved = true;
+                step++;
+            } else if (clusterset.build(c)) {
+                step++;
+                moveClusterForward(i, c);
+                somethingMoved = true;
+            }
+        }
+        i++;
+        if (i is allClusters.size() && somethingMoved) {
+            i = 0;
+            somethingMoved = false;
+        }
+    }
+}
+
+//bool balanceClusterSets() {
+//  for (int i = 0; i < allClusters.size(); i++) {
+//      NodeCluster c = (NodeCluster)allClusters.get(i);
+//      if (c.weightedTotal < 0 && c.leftFreedom is 0) {
+//          if (clusterset.build(c)) {
+//              moveClusterForward(i, c);
+//              return true;
+//          }
+//      } else if (c.weightedTotal > 0 && c.rightFreedom is 0) {
+//          if (clusterset.build(c)) {
+//              moveClusterForward(i, c);
+//              return true;
+//          }
+//      }
+//  }
+//  return false;
+//}
+
+void buildGPrime() {
+    RankList ranks = graph.ranks;
+    buildRankSeparators(ranks);
+
+    Rank rank;
+    Node n;
+    for (int r = 1; r < ranks.size(); r++) {
+        rank = ranks.getRank(r);
+        for (int i = 0; i < rank.count(); i++) {
+            n = rank.getNode(i);
+            addEdges(n);
+        }
+    }
+}
+
+void buildRankSeparators(RankList ranks) {
+    Rank rank;
+    Node n, nPrime, prevNPrime;
+    Edge e;
+    for (int r = 0; r < ranks.size(); r++) {
+        rank = ranks.getRank(r);
+        prevNPrime = null;
+        for (int i = 0; i < rank.count(); i++) {
+            n = rank.getNode(i);
+            nPrime = new Node(n);
+            if (i is 0) {
+                e = new Edge(graphLeft, nPrime, 0, 0);
+                prime.edges.add(e);
+                e.delta = graph.getPadding(n).left + graph.getMargin().left;
+            } else {
+                e = new Edge(prevNPrime, nPrime);
+                e.weight = 0;
+                prime.edges.add(e);
+                rowSeparation(e);
+            }
+            prevNPrime = nPrime;
+            prime.nodes.add(nPrime);
+            map(n, nPrime);
+            if (i is rank.count() - 1) {
+                e = new Edge(nPrime, graphRight, 0, 0);
+                e.delta = n.width + graph.getPadding(n).right + graph.getMargin().right;
+                prime.edges.add(e);
+            }
+        }
+    }
+}
+
+private void calculateCellLocations() {
+    graph.cellLocations = new int[][](graph.ranks.size() + 1);
+    for (int row = 0; row < graph.ranks.size(); row++) {
+        Rank rank = graph.ranks.getRank(row);
+        int locations[] = graph.cellLocations[row] = new int[rank.size() + 1];
+        int cell;
+        Node node = null;
+        for (cell = 0; cell < rank.size(); cell++) {
+            node = rank.getNode(cell);
+            locations[cell] = node.x - graph.getPadding(node).left;
+        }
+        locations[cell] = node.x + node.width + graph.getPadding(node).right;
+    }
+}
+
+private void findAllClusters() {
+    Node root = prime.nodes.getNode(0);
+    NodeCluster cluster = new NodeCluster();
+    allClusters = new ArrayList();
+    allClusters.add(cluster);
+    growCluster(root, cluster);
+
+    for (int i = 0; i < prime.edges.size(); i++) {
+        Edge e = prime.edges.getEdge(i);
+        NodeCluster sourceCluster = cast(NodeCluster)clusterMap.get(e.source);
+        NodeCluster targetCluster = cast(NodeCluster)clusterMap.get(e.target);
+
+        //Ignore cluster internal edges
+        if (targetCluster is sourceCluster)
+            continue;
+
+        CollapsedEdges link = sourceCluster.getRightNeighbor(targetCluster);
+        if (link is null) {
+            link = new CollapsedEdges(e);
+            sourceCluster.addRightNeighbor(targetCluster, link);
+            targetCluster.addLeftNeighbor(sourceCluster, link);
+        } else {
+            prime.removeEdge(link.processEdge(e));
+            i--;
+        }
+    }
+    for (int i = 0; i < allClusters.size(); i++)
+        (cast(NodeCluster)allClusters.get(i)).initValues();
+}
+
+Node get(Node key) {
+    return cast(Node)map_.get(key);
+}
+
+void growCluster(Node root, NodeCluster cluster) {
+    cluster.add(root);
+    clusterMap.put(root, cluster);
+    EdgeList treeChildren = getSpanningTreeChildren(root);
+    for (int i = 0; i < treeChildren.size(); i++) {
+        Edge e = treeChildren.getEdge(i);
+        if (e.cut !is 0)
+            growCluster(getTreeTail(e), cluster);
+        else {
+            NodeCluster newCluster = new NodeCluster();
+            allClusters.add(newCluster);
+            growCluster(getTreeTail(e), newCluster);
+        }
+    }
+}
+
+void map(Node key, Node value) {
+    map_.put(key, value);
+}
+
+private void moveClusterForward(int i, NodeCluster c) {
+    if (i is 0)
+        return;
+    int swapIndex = i / 2;
+    Object temp = allClusters.get(swapIndex);
+    allClusters.set(swapIndex, c);
+    allClusters.set(i, temp);
+}
+
+void refreshDirtyClusters() {
+    for (Iterator iter = dirtyClusters.iterator(); iter.hasNext();)
+        (cast(NodeCluster)iter.next()).refreshValues();
+    dirtyClusters.clear();
+}
+
+void rowSeparation(Edge e) {
+    Node source = cast(Node)e.source.data;
+    Node target = cast(Node)e.target.data;
+    e.delta = source.width
+        + graph.getPadding(source).right
+        + graph.getPadding(target).left;
+}
+
+public void visit(DirectedGraph g) {
+    graph = g;
+    prime = new DirectedGraph();
+    prime.nodes.add(graphLeft = new Node(cast(Object)null));
+    prime.nodes.add(graphRight = new Node(cast(Object)null));
+    if (g.tensorStrength !is 0)
+        prime.edges.add(new Edge(graphLeft, graphRight, g.tensorSize, g.tensorStrength));
+    buildGPrime();
+    (new InitialRankSolver())
+        .visit(prime);
+    (new TightSpanningTreeSolver())
+        .visit(prime);
+
+    RankAssignmentSolver solver = new RankAssignmentSolver();
+    solver.visit(prime);
+    graph.size.width = graphRight.rank;
+    balanceClusters();
+
+    prime.nodes.adjustRank(-graphLeft.rank);
+    applyGPrime();
+    calculateCellLocations();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/InitialRankSolver.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.InitialRankSolver;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.draw2d.graph.GraphVisitor;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.NodeList;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.EdgeList;
+import dwtx.draw2d.graph.Edge;
+
+/**
+ * Assigns a valid rank assignment to all nodes based on their edges.  The assignment is
+ * not optimal in that it does not provide the minimum global length of edge lengths.
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+class InitialRankSolver : GraphVisitor {
+
+protected DirectedGraph graph;
+protected EdgeList candidates;
+protected NodeList members;
+
+public this(){
+    candidates = new EdgeList();
+    members = new NodeList();
+}
+
+public void visit(DirectedGraph graph) {
+    this.graph = graph;
+    graph.edges.resetFlags(false);
+    graph.nodes.resetFlags();
+    solve();
+}
+
+protected void solve() {
+    if (graph.nodes.size() is 0)
+        return;
+    NodeList unranked = new NodeList(graph.nodes);
+    NodeList rankMe = new NodeList();
+    Node node;
+    int i;
+    while (!unranked.isEmpty()) {
+        rankMe.clear();
+        for (i = 0; i < unranked.size();) {
+            node = unranked.getNode(i);
+            if (node.incoming.isCompletelyFlagged()) {
+                rankMe.add(node);
+                unranked.remove(i);
+            } else
+                i++;
+        }
+        if (rankMe.size() is 0)
+            throw new RuntimeException("Cycle detected in graph"); //$NON-NLS-1$
+        for (i = 0; i < rankMe.size(); i++) {
+            node = rankMe.getNode(i);
+            assignMinimumRank(node);
+            node.outgoing.setFlags(true);
+        }
+    }
+
+    connectForest();
+}
+
+private void connectForest() {
+    List forest = new ArrayList();
+    Stack stack = new Stack();
+    NodeList tree;
+    graph.nodes.resetFlags();
+    for (int i = 0; i < graph.nodes.size(); i++) {
+        Node neighbor, n = graph.nodes.getNode(i);
+        if (n.flag)
+            continue;
+        tree = new NodeList();
+        stack.push(n);
+        while (!stack.isEmpty()) {
+            n = cast(Node) stack.pop();
+            n.flag = true;
+            tree.add(n);
+            for (int s = 0; s < n.incoming.size(); s++) {
+                neighbor = n.incoming.getEdge(s).source;
+                if (!neighbor.flag)
+                    stack.push(neighbor);
+            }
+            for (int s = 0; s < n.outgoing.size(); s++) {
+                neighbor = n.outgoing.getEdge(s).target;
+                if (!neighbor.flag)
+                    stack.push(neighbor);
+            }
+        }
+        forest.add(tree);
+    }
+
+    if (forest.size() > 1) {
+        //connect the forest
+        graph.forestRoot = new Node(stringcast("the forest root")); //$NON-NLS-1$
+        graph.nodes.add(graph.forestRoot);
+        for (int i = 0; i < forest.size(); i++) {
+            tree = cast(NodeList) forest.get(i);
+            graph.edges.add(new Edge(graph.forestRoot, tree.getNode(0), 0, 0));
+        }
+    }
+}
+
+private void assignMinimumRank(Node node) {
+    int rank = 0;
+    Edge e;
+    for (int i1 = 0; i1 < node.incoming.size(); i1++) {
+        e = node.incoming.getEdge(i1);
+        rank = Math.max(rank, e.delta + e.source.rank);
+    }
+    node.rank = rank;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/InvertEdges.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.InvertEdges;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.graph.GraphVisitor;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.Edge;
+
+/**
+ * Inverts any edges which are marked as backwards or "feedback" edges.
+ *
+ * @author Daniel Lee
+ * @since 2.1.2
+ */
+class InvertEdges : GraphVisitor {
+
+/**
+ *
+ * @see GraphVisitor#visit(dwtx.draw2d.graph.DirectedGraph)
+ */
+public void visit(DirectedGraph g) {
+    for (int i = 0; i < g.edges.size(); i++) {
+        Edge e = g.edges.getEdge(i);
+        if (e.isFeedback)
+            e.invert();
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/LocalOptimizer.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,124 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.LocalOptimizer;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.graph.GraphVisitor;
+import dwtx.draw2d.graph.GraphUtilities;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.Rank;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.EdgeList;
+import dwtx.draw2d.graph.Edge;
+
+/**
+ * This graph visitor examines all adjacent pairs of nodes and determines if
+ * swapping the two nodes provides improved graph aesthetics.
+ * @author Daniel Lee
+ * @since 2.1.2
+ */
+class LocalOptimizer : GraphVisitor {
+
+bool shouldSwap(Node current, Node next) {
+    if (GraphUtilities.isConstrained(current, next))
+        return false;
+    int crossCount = 0;
+    int invertedCrossCount = 0;
+
+    EdgeList currentEdges = current.incoming;
+    EdgeList nextEdges = next.incoming;
+    int rank = current.rank - 1;
+    int iCurrent, iNext;
+
+    for (int i = 0; i < currentEdges.size(); i++) {
+        Edge currentEdge = currentEdges.getEdge(i);
+        iCurrent = currentEdge.getIndexForRank(rank);
+        for (int j = 0; j < nextEdges.size(); j++) {
+            iNext = nextEdges.getEdge(j).getIndexForRank(rank);
+            if (iNext < iCurrent)
+                crossCount++;
+            else if (iNext > iCurrent)
+                invertedCrossCount++;
+            else {
+                //edges go to the same location
+                int offsetDiff = nextEdges.getEdge(j).getSourceOffset()
+                        - currentEdge.getSourceOffset();
+                if (offsetDiff < 0)
+                    crossCount++;
+                else if (offsetDiff > 0)
+                    invertedCrossCount++;
+            }
+        }
+    }
+
+    currentEdges = current.outgoing;
+    nextEdges = next.outgoing;
+    rank = current.rank + 1;
+
+    for (int i = 0; i < currentEdges.size(); i++) {
+        Edge currentEdge = currentEdges.getEdge(i);
+        iCurrent = currentEdge.getIndexForRank(rank);
+        for (int j = 0; j < nextEdges.size(); j++) {
+            iNext = nextEdges.getEdge(j).getIndexForRank(rank);
+            if (iNext < iCurrent)
+                crossCount++;
+            else if (iNext > iCurrent)
+                invertedCrossCount++;
+            else {
+                //edges go to the same location
+                int offsetDiff = nextEdges.getEdge(j).getTargetOffset()
+                        - currentEdge.getTargetOffset();
+                if (offsetDiff < 0)
+                    crossCount++;
+                else if (offsetDiff > 0)
+                    invertedCrossCount++;
+            }
+        }
+    }
+    if (invertedCrossCount < crossCount)
+        return true;
+    return false;
+}
+
+private void swapNodes(Node current, Node next, Rank rank) {
+    int index = rank.indexOf(current);
+    rank.set(index + 1, current);
+    rank.set(index, next);
+    index = current.index;
+    current.index = next.index;
+    next.index = index;
+}
+
+/**
+ * @see GraphVisitor#visit(dwtx.draw2d.graph.DirectedGraph)
+ */
+public void visit(DirectedGraph g) {
+    bool flag;
+    do {
+        flag = false;
+        for (int r = 0; r < g.ranks.size(); r++) {
+            Rank rank = g.ranks.getRank(r);
+            for (int n = 0; n < rank.count() - 1; n++) {
+                Node currentNode = rank.getNode(n);
+                Node nextNode = rank.getNode(n + 1);
+                if (shouldSwap(currentNode, nextNode)) {
+                    swapNodes(currentNode, nextNode, rank);
+                    flag = true;
+                    n = Math.max(0, n - 2);
+                }
+            }
+        }
+    } while (flag);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/MinCross.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.MinCross;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.graph.RankSorter;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.GraphVisitor;
+import dwtx.draw2d.graph.Rank;
+
+/**
+ * Sweeps up and down the ranks rearranging them so as to reduce edge crossings.
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+class MinCross : GraphVisitor {
+
+static const int MAX = 45;
+
+private DirectedGraph g;
+private RankSorter sorter;
+
+public this() {
+    sorter = new RankSorter();
+}
+
+/**
+ * @since 3.1
+ */
+public this(RankSorter sorter) {
+    this();
+    setRankSorter(sorter);
+}
+
+public void setRankSorter(RankSorter sorter) {
+    this.sorter = sorter;
+}
+
+void solve() {
+    Rank rank;
+    for (int loop = 0; loop < MAX; loop++) {
+        for (int row = 1; row < g.ranks.size(); row++) {
+            rank = g.ranks.getRank(row);
+            sorter.sortRankIncoming(g, rank, row, cast(double)loop / MAX);
+        }
+        if (loop is MAX - 1)
+            continue;
+        for (int row = g.ranks.size() - 2; row >= 0; row--) {
+            rank = g.ranks.getRank(row);
+            sorter.sortRankOutgoing(g, rank, row, cast(double)loop / MAX);
+        }
+    }
+}
+
+/**
+ *  @see GraphVisitor#visit(dwtx.draw2d.graph.DirectedGraph)
+ */
+public void visit(DirectedGraph g) {
+    sorter.init(g);
+    this.g = g;
+    solve();
+    sorter.optimize(g);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/NestingTree.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.NestingTree;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.Rank;
+import dwtx.draw2d.graph.Subgraph;
+
+class NestingTree {
+
+List contents;
+bool isLeaf = true;
+int size;
+double sortValue;
+Node subgraph;
+
+public this(){
+    contents = new ArrayList();
+}
+private static void addToNestingTree(Map map, Node child) {
+    Subgraph subgraph = child.getParent();
+    NestingTree parent = cast(NestingTree)map.get(subgraph);
+    if (parent is null) {
+        parent = new NestingTree();
+        parent.subgraph = subgraph;
+        map.put(subgraph, parent);
+        if (subgraph !is null)
+            addToNestingTree(map, parent);
+    }
+    parent.contents.add(child);
+}
+
+private static void addToNestingTree(Map map, NestingTree branch) {
+    Subgraph subgraph = branch.subgraph.getParent();
+    NestingTree parent = cast(NestingTree)map.get(subgraph);
+    if (parent is null) {
+        parent = new NestingTree();
+        parent.subgraph = subgraph;
+        map.put(subgraph, parent);
+        if (subgraph !is null)
+            addToNestingTree(map, parent);
+    }
+    parent.contents.add(branch);
+}
+
+static NestingTree buildNestingTreeForRank(Rank rank) {
+    Map nestingMap = new HashMap();
+
+    for (int j = 0; j < rank.count(); j++) {
+        Node node = rank.getNode(j);
+        addToNestingTree(nestingMap, node);
+    }
+
+    return cast(NestingTree)nestingMap.get(null);
+}
+
+void calculateSortValues() {
+    int total = 0;
+    for (int i = 0; i < contents.size(); i++) {
+        Object o = contents.get(i);
+        if ( auto e = cast(NestingTree)o ) {
+            isLeaf = false;
+            e.calculateSortValues();
+            total += cast(int)(e.sortValue * e.size);
+            size += e.size;
+        } else {
+            Node n = cast(Node)o;
+            n.sortValue = n.index;
+            total += n.index;
+            size++;
+        }
+    }
+    sortValue = cast(double)total / size;
+}
+
+void getSortValueFromSubgraph() {
+    if (subgraph !is null)
+        sortValue = subgraph.sortValue;
+    for (int i = 0; i < contents.size(); i++) {
+        Object o = contents.get(i);
+        if (auto nt = cast(NestingTree)o )
+            nt.getSortValueFromSubgraph();
+    }
+}
+
+void recursiveSort(bool sortLeaves) {
+    if (isLeaf && !sortLeaves)
+        return;
+    bool change = false;
+    //Use modified bubble sort for almost-sorted lists.
+    do {
+        change = false;
+        for (int i = 0; i < contents.size() - 1; i++)
+            change |= swap(i);
+        if (!change)
+            break;
+        change = false;
+        for (int i = contents.size() - 2; i >= 0; i--)
+            change |= swap(i);
+    } while (change);
+    for (int i = 0; i < contents.size(); i++) {
+        Object o = contents.get(i);
+        if (auto nt = cast(NestingTree)o )
+            nt.recursiveSort(sortLeaves);
+    }
+}
+
+void repopulateRank(Rank r) {
+    for (int i = 0; i < contents.size(); i++) {
+        Object o = contents.get(i);
+        if (null !is cast(Node)o )
+            r.add(o);
+        else
+            (cast(NestingTree)o).repopulateRank(r);
+    }
+}
+
+
+bool swap(int index) {
+    Object left = contents.get(index);
+    Object right = contents.get(index + 1);
+    double iL = (null !is cast(Node)left )
+        ? (cast(Node)left).sortValue
+        : (cast(NestingTree)left).sortValue;
+    double iR = (null !is cast(Node)right )
+        ? (cast(Node)right).sortValue
+        : (cast(NestingTree)right).sortValue;
+    if (iL <= iR)
+        return false;
+    contents.set(index, right);
+    contents.set(index + 1, left);
+    return true;
+}
+
+public String toString() {
+    if( subgraph )
+        return "Nesting:" ~ subgraph.toString; //$NON-NLS-1$
+    return "Nesting: null"; //$NON-NLS-1$
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/Node.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,332 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.Node;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import tango.text.convert.Format;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.graph.EdgeList;
+import dwtx.draw2d.graph.Subgraph;
+import dwtx.draw2d.graph.Edge;
+
+/**
+ * A node in a DirectedGraph. A node has 0 or more incoming and outgoing {@link Edge}s. A
+ * node is given a width and height by the client. When a layout places the node in the
+ * graph, it will determine the node's x and y location.  It may also modify the node's
+ * height.
+ *
+ * A node represents both the <EM>input</EM> and the <EM>output</EM> for a layout
+ * algorithm. The following fields are used as input to a graph layout:
+ * <UL>
+ *   <LI>{@link #width} - the node's width.
+ *   <LI>{@link #height} - the node's height.
+ *   <LI>{@link #outgoing} - the node's outgoing edges.
+ *   <LI>{@link #incoming} - the node's incoming edges.
+ *   <LI>padding - the amount of space to be left around the outside of the node.
+ *   <LI>{@link #incomingOffset} - the default attachment point for incoming edges.
+ *   <LI>{@link #outgoingOffset} - the default attachment point for outgoing edges.
+ *   <LI>parent - the parent subgraph containing this node.
+ * </UL>
+ * <P>
+ * The following fields are calculated by a graph layout and comprise the <EM>output</EM>:
+ * <UL>
+ *   <LI>{@link #x} - the node's x location
+ *   <LI>{@link #y} - the node's y location
+ *   <LI>{@link #height} - the node's height may be stretched to match the height of other
+ *   nodes
+ * </UL>
+ *
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+public class Node {
+
+Node left, right;
+
+Object[] workingData;
+int[] workingInts;
+
+/**
+ * Clients may use this field to mark the Node with an arbitrary data object.
+ */
+public Object data;
+
+//used by various graph visitors
+bool flag;
+
+/**
+ * The height of this node. This value should be set prior to laying out the directed
+ * graph.  Depending on the layout rules, a node's height may be expanded to match the
+ * height of other nodes around it.
+ */
+public int height = 40;
+
+/**
+ * @deprecated use {@link #setRowConstraint(int)} and {@link #getRowConstraint()}
+ */
+public int rowOrder = -1;
+
+/**
+ * The edges for which this node is the target.
+ */
+public EdgeList incoming;
+
+/**
+ * The default attachment point for incoming edges.  <code>-1</code> indicates that the
+ * node's horizontal center should be used.
+ */
+public int incomingOffset = -1;
+
+// A non-decreasing number given to consecutive nodes in a Rank.
+int index;
+
+//Used in Compound graphs to quickly determine whether a node is inside a subgraph.
+int nestingIndex = -1;
+
+/**
+ * The edges for which this node is the source.
+ */
+public EdgeList outgoing;
+
+Insets padding;
+private Subgraph parent;
+int rank;
+
+/**
+ * @deprecated for internal use only
+ */
+public double sortValue;
+
+/**
+ * The node's outgoing offset attachment point.
+ */
+public int outgoingOffset = -1;
+
+/**
+ * The node's width. The default value is 50.
+ */
+public int width = 50;
+
+/**
+ * The node's x coordinate.
+ */
+public int x;
+/**
+ * The node's y coordinate.
+ */
+public int y;
+
+/**
+ * Constructs a new node.
+ */
+public this() { }
+
+/**
+ * Constructs a node with the given data object
+ * @param data an arbitrary data object
+ */
+public this(Object data) {
+    this(data, null);
+}
+
+/**
+ * Constructs a node inside the given subgraph.
+ * @param parent the parent subgraph
+ */
+public this(Subgraph parent) {
+    this(null, parent);
+}
+
+/**
+ * Constructs a node with the given data object and parent subgraph. This node is added to
+ * the set of members for the parent subgraph
+ * @param data an arbitrary data object
+ * @param parent the parent subgraph or <code>null</code>
+ */
+public this(Object data, Subgraph parent) {
+    this.data = data;
+    this.parent = parent;
+    incoming = new EdgeList();
+    outgoing = new EdgeList();
+    workingData = new Object[3];
+    workingInts = new int[4];
+    if (parent !is null)
+        parent.addMember(this);
+}
+
+/**
+ * Returns the incoming attachment point.  This is the distance from the left edge to the
+ * default incoming attachment point for edges.  Each incoming edge may have it's own
+ * attachment setting which takes priority over this default one.
+ * @return the incoming offset
+ */
+public int getOffsetIncoming() {
+    if (incomingOffset is -1)
+        return width / 2;
+    return incomingOffset;
+}
+
+/**
+ * Returns the outgoing attachment point.  This is the distance from the left edge to the
+ * default outgoing attachment point for edges.  Each outgoing edge may have it's own
+ * attachment setting which takes priority over this default one.
+ * @return the outgoing offset
+ */
+public int getOffsetOutgoing() {
+    if (outgoingOffset is -1)
+        return width / 2;
+    return outgoingOffset;
+}
+
+/**
+ * Returns the padding for this node or <code>null</code> if the default padding for the
+ * graph should be used.
+ * @return the padding or <code>null</code>
+ */
+public Insets getPadding() {
+    return padding;
+}
+
+/**
+ * Returns the parent Subgraph or <code>null</code> if there is no parent. Subgraphs are
+ * only for use in {@link CompoundDirectedGraphLayout}.
+ * @return the parent or <code>null</code>
+ */
+public Subgraph getParent() {
+    return parent;
+}
+
+/**
+ * For internal use only. Returns <code>true</code> if the given node is equal to this
+ * node.  This method is implemented for consitency with Subgraph.
+ * @param node the node in question
+ * @return <code>true</code> if nested
+ */
+bool isNested(Node node) {
+    return node is this;
+}
+
+/**
+ * Sets the padding. <code>null</code> indicates that the default padding should be used.
+ * @param padding an insets or <code>null</code>
+ */
+public void setPadding(Insets padding) {
+    this.padding = padding;
+}
+
+/**
+ * Sets the parent subgraph.  This method should not be called directly.  The constructor
+ * will set the parent accordingly.
+ * @param parent the parent
+ */
+public void setParent(Subgraph parent) {
+    this.parent = parent;
+}
+
+/**
+ * Sets the row sorting constraint for this node. By default, a node's constraint is
+ * <code>-1</code>. If two nodes have different values both >= 0, the node with the
+ * smaller constraint will be placed to the left of the other node. In all other cases no
+ * relative placement is guaranteed.
+ * @param value the row constraint
+ * @since 3.2
+ */
+public void setRowConstraint(int value) {
+    this.rowOrder = value;
+}
+
+/**
+ * Returns the row constraint for this node.
+ * @return the row constraint
+ * @since 3.2
+ */
+public int getRowConstraint() {
+    return rowOrder;
+}
+
+/**
+ * Sets the size of this node to the given dimension.
+ * @param size the new size
+ * @since 3.2
+ */
+public void setSize(Dimension size) {
+    width = size.width;
+    height = size.height;
+}
+
+/**
+ * @see Object#toString()
+ */
+public String toString() {
+    return Format("N({})", data ); //$NON-NLS-1$ //$NON-NLS-2$
+}
+
+Iterator iteratorNeighbors() {
+    return new class(outgoing) Iterator {
+        int offset;
+        EdgeList list;
+        this(EdgeList a){
+            list = a;
+        }
+        public Object next() {
+            Edge edge = list.getEdge(offset++);
+            if (offset < list.size())
+                return edge.opposite(this.outer);
+            if (list is outgoing) {
+                list = incoming;
+                offset = 0;
+            } else
+                list = null;
+            return edge.opposite(this.outer);
+        }
+
+        public bool hasNext() {
+            if (list is null)
+                return false;
+            if (offset < list.size())
+                return true;
+            if (list is outgoing) {
+                list = incoming;
+                offset = 0;
+            }
+            return offset < list.size();
+        }
+
+        public void remove() {
+            throw new RuntimeException("Remove not supported"); //$NON-NLS-1$
+        }
+    };
+}
+
+/**
+ * Returns a reference to a node located left from this one
+ * @return <code>Node</code> on the left from this one
+ * @since 3.4
+ */
+public Node getLeft() {
+    return left;
+}
+
+/**
+ * Returns a reference to a node located right from this one
+ * @return <code>Node</code> on the right from this one
+ * @since 3.4
+ */
+public Node getRight() {
+    return right;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/NodeCluster.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,243 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.NodeCluster;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.draw2d.graph.NodeList;
+import dwtx.draw2d.graph.CollapsedEdges;
+
+/**
+ * A group of nodes which are interlocked and cannot be separately placed.
+ * @since 3.1
+ */
+class NodeCluster : NodeList {
+
+alias NodeList.adjustRank adjustRank;
+
+int toHash_;
+
+bool isSetMember;
+bool isDirty;
+bool leftDirty;
+bool rightDirty;
+
+int leftFreedom;
+int rightFreedom;
+int leftNonzero;
+int rightNonzero;
+int leftCount = 0;
+int rightCount = 0;
+
+CollapsedEdges[] leftLinks;
+CollapsedEdges[] rightLinks;
+NodeCluster[] leftNeighbors;
+NodeCluster[] rightNeighbors;
+
+int effectivePull;
+int weightedTotal;
+int weightedDivisor;
+int unweightedTotal;
+int unweightedDivisor;
+
+public this(){
+    toHash_ = (new Object()).toHash();
+    leftLinks = new CollapsedEdges[10];
+    rightLinks = new CollapsedEdges[10];
+    leftNeighbors = new NodeCluster[10];
+    rightNeighbors = new NodeCluster[10];
+}
+
+void addLeftNeighbor(NodeCluster neighbor, CollapsedEdges link) {
+    //Need to grow array in the following case
+    if (leftNeighbors.length is leftCount) {
+        int newSize = leftNeighbors.length * 2;
+
+        NodeCluster newNeighbors[] = new NodeCluster[newSize];
+        CollapsedEdges newLinks[] = new CollapsedEdges[newSize];
+
+        System.arraycopy(leftNeighbors, 0, newNeighbors, 0, leftNeighbors.length);
+        System.arraycopy(leftLinks, 0, newLinks, 0, leftLinks.length);
+
+        leftNeighbors = newNeighbors;
+        leftLinks = newLinks;
+    }
+    leftNeighbors[leftCount] = neighbor;
+    leftLinks[leftCount++] = link;
+}
+
+void addRightNeighbor(NodeCluster neighbor, CollapsedEdges link) {
+    if (rightNeighbors.length is rightCount) {
+        int newSize = rightNeighbors.length * 2;
+
+        NodeCluster newNeighbors[] = new NodeCluster[newSize];
+        CollapsedEdges newLinks[] = new CollapsedEdges[newSize];
+
+        System.arraycopy(rightNeighbors, 0, newNeighbors, 0, rightNeighbors.length);
+        System.arraycopy(rightLinks, 0, newLinks, 0, rightLinks.length);
+
+        rightNeighbors = newNeighbors;
+        rightLinks = newLinks;
+    }
+    rightNeighbors[rightCount] = neighbor;
+    rightLinks[rightCount++] = link;
+}
+
+public void adjustRank(int delta, Collection affected) {
+    adjustRank(delta);
+    NodeCluster neighbor;
+    CollapsedEdges edges;
+    for (int i = 0; i < leftCount; i++) {
+        neighbor = leftNeighbors[i];
+        if (neighbor.isSetMember)
+            continue;
+        edges = leftLinks[i];
+
+        neighbor.weightedTotal += delta * edges.collapsedWeight;
+        neighbor.unweightedTotal += delta * edges.collapsedCount;
+
+        weightedTotal -= delta * edges.collapsedWeight;
+        unweightedTotal -= delta * edges.collapsedCount;
+
+        neighbor.rightDirty = leftDirty = true;
+        if (!neighbor.isDirty) {
+            neighbor.isDirty = true;
+            affected.add(neighbor);
+        }
+    }
+    for (int i = 0; i < rightCount; i++) {
+        neighbor = rightNeighbors[i];
+        if (neighbor.isSetMember)
+            continue;
+        edges = rightLinks[i];
+
+        neighbor.weightedTotal += delta * edges.collapsedWeight;
+        neighbor.unweightedTotal += delta * edges.collapsedCount;
+
+        weightedTotal -= delta * edges.collapsedWeight;
+        unweightedTotal -= delta * edges.collapsedCount;
+
+        neighbor.leftDirty = rightDirty = true;
+        if (!neighbor.isDirty) {
+            neighbor.isDirty = true;
+            affected.add(neighbor);
+        }
+    }
+    isDirty = true;
+    affected.add(this);
+}
+
+public override int opEquals(Object o) {
+    return o is this;
+}
+
+CollapsedEdges getLeftNeighbor(NodeCluster neighbor) {
+    for (int i = 0; i < leftCount; i++) {
+        if (leftNeighbors[i] is neighbor)
+            return leftLinks[i];
+    }
+    return null;
+}
+
+int getPull() {
+    return effectivePull;
+}
+
+CollapsedEdges getRightNeighbor(NodeCluster neighbor) {
+    for (int i = 0; i < rightCount; i++) {
+        if (rightNeighbors[i] is neighbor)
+            return rightLinks[i];
+    }
+    return null;
+}
+
+public override hash_t toHash() {
+    return toHash_;
+}
+
+/**
+ * Initializes pull and freedom values.
+ */
+void initValues() {
+    weightedTotal = 0;
+    weightedDivisor = 0;
+    unweightedTotal = 0;
+    int slack;
+
+    leftNonzero = rightNonzero = leftFreedom = rightFreedom = Integer.MAX_VALUE;
+    for (int i = 0; i < leftCount; i++) {
+        CollapsedEdges edges = leftLinks[i];
+        weightedTotal -= edges.getWeightedPull();
+        unweightedTotal -= edges.tightestEdge.getSlack();
+        unweightedDivisor += edges.collapsedCount;
+        weightedDivisor += edges.collapsedWeight;
+        slack = edges.tightestEdge.getSlack();
+        leftFreedom = Math.min(slack, leftFreedom);
+        if (slack > 0)
+            leftNonzero = Math.min(slack, leftNonzero);
+    }
+    for (int i = 0; i < rightCount; i++) {
+        CollapsedEdges edges = rightLinks[i];
+        weightedTotal += edges.getWeightedPull();
+        unweightedDivisor += edges.collapsedCount;
+        unweightedTotal += edges.tightestEdge.getSlack();
+        weightedDivisor += edges.collapsedWeight;
+        slack = edges.tightestEdge.getSlack();
+        rightFreedom = Math.min(slack, rightFreedom);
+        if (slack > 0)
+            rightNonzero = Math.min(slack, rightNonzero);
+    }
+    updateEffectivePull();
+}
+
+/**
+ * Refreshes the left and right freedom.
+ */
+void refreshValues() {
+    int slack;
+    isDirty = false;
+    if (leftDirty) {
+        leftDirty = false;
+        leftNonzero = leftFreedom = Integer.MAX_VALUE;
+        for (int i = 0; i < leftCount; i++) {
+            CollapsedEdges edges = leftLinks[i];
+            slack = edges.tightestEdge.getSlack();
+            leftFreedom = Math.min(slack, leftFreedom);
+            if (slack > 0)
+                leftNonzero = Math.min(slack, leftNonzero);
+        }
+    }
+    if (rightDirty) {
+        rightDirty = false;
+        rightNonzero = rightFreedom = Integer.MAX_VALUE;
+        for (int i = 0; i < rightCount; i++) {
+            CollapsedEdges edges = rightLinks[i];
+            slack = edges.tightestEdge.getSlack();
+            rightFreedom = Math.min(slack, rightFreedom);
+            if (slack > 0)
+                rightNonzero = Math.min(slack, rightNonzero);
+        }
+    }
+    updateEffectivePull();
+}
+
+private void updateEffectivePull() {
+    if (weightedDivisor !is 0)
+        effectivePull = weightedTotal / weightedDivisor;
+    else if (unweightedDivisor !is 0)
+            effectivePull = unweightedTotal / unweightedDivisor;
+    else
+        effectivePull = 0;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/NodeList.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.NodeList;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.draw2d.graph.Node;
+
+/**
+ * A list containing nodes.
+ * @author hudsonr
+ * @since 2.1.2
+ */
+public class NodeList : ArrayList {
+
+/**
+ * Constructs an empty NodeList.
+ */
+public this() { }
+
+/**
+ * Constructs a NodeList with the elements from the specified list.
+ * @param list the list whose elements are to be added to this list
+ */
+public this(NodeList list) {
+    super(list);
+}
+
+void adjustRank (int delta) {
+    if (delta is 0)
+        return;
+    for (int i = 0; i < size(); i++)
+        getNode(i).rank += delta;
+}
+
+void resetSortValues() {
+    for (int i = 0; i < size(); i++)
+        getNode(i).sortValue = 0.0;
+}
+
+void resetIndices() {
+    for (int i = 0; i < size(); i++)
+        getNode(i).index = 0;
+}
+
+void normalizeRanks() {
+    int minRank = Integer.MAX_VALUE;
+    for (int i = 0; i < size(); i++)
+        minRank = Math.min(minRank, getNode(i).rank);
+    adjustRank(-minRank);
+}
+
+/**
+ * Returns the Node at the given index.
+ * @param index the index
+ * @return the node at a given index
+ */
+public Node getNode(int index) {
+    return cast(Node)super.get(index);
+}
+
+void resetFlags() {
+    for (int i = 0; i < size(); i++) {
+        getNode(i).flag = false;
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/NodePair.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.NodePair;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.graph.Node;
+import tango.text.convert.Format;
+
+/**
+ * @author hudsonr
+ * @since 2.1
+ */
+class NodePair {
+
+public Node n1;
+public Node n2;
+
+public this() { }
+
+public this(Node n1, Node n2) {
+    this.n1 = n1;
+    this.n2 = n2;
+}
+
+public override int opEquals(Object obj) {
+    if (auto np = cast(NodePair) obj ) {
+        return np.n1 is n1 && np.n2 is n2;
+    }
+    return false;
+}
+
+public override hash_t toHash() {
+    return n1.toHash() ^ n2.toHash();
+}
+
+/**
+ * @see java.lang.Object#toString()
+ */
+public String toString() {
+    return Format("[{}, {}]", n1, n2 ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/Obstacle.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.Obstacle;
+
+import dwt.dwthelper.utils;
+import tango.text.convert.Format;
+
+import dwtx.draw2d.PositionConstants;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.graph.Vertex;
+import dwtx.draw2d.graph.ShortestPathRouter;
+
+/**
+ * An obstacle representation for the ShortestPathRouting. This is a subclass of Rectangle.
+ *
+ * This class is for internal use only.
+ * @author Whitney Sorenson
+ * @since 3.0
+ */
+class Obstacle
+    : Rectangle
+{
+
+bool exclude;
+Vertex topLeft, topRight, bottomLeft, bottomRight, center;
+private ShortestPathRouter router;
+
+/**
+ * Creates a new obstacle from the given rectangle bounds.
+ * @param rect the bounds
+ */
+this(Rectangle rect, ShortestPathRouter router) {
+    init(rect);
+    this.router = router;
+}
+
+/**
+ * Returns <code>true</code> if the given point is contained but not on the boundary of
+ * this obstacle.
+ * @param p a point
+ * @return <code>true</code> if properly contained
+ */
+public bool containsProper(Point p) {
+    return p.x > this.x
+        && p.x < this.x + this.width - 1
+        && p.y > this.y
+        && p.y < this.y + this.height - 1;
+}
+
+public int getSpacing() {
+    return router.getSpacing();
+}
+
+private void growVertex(Vertex vertex) {
+    if (vertex.totalCount > 0)
+        vertex.grow();
+}
+
+/**
+ * Grows all vertices on this obstacle.
+ */
+void growVertices() {
+    growVertex(topLeft);
+    growVertex(topRight);
+    growVertex(bottomLeft);
+    growVertex(bottomRight);
+}
+
+/**
+ * Initializes this obstacle to the values of the given rectangle
+ *
+ * @param rect bounds of this obstacle
+ */
+void init(Rectangle rect) {
+    this.x = rect.x;
+    this.y = rect.y;
+    this.width = rect.width;
+    this.height = rect.height;
+
+    exclude = false;
+
+    topLeft = new Vertex(x, y, this);
+    topLeft.positionOnObstacle = PositionConstants.NORTH_WEST;
+    topRight = new Vertex(x + width - 1, y, this);
+    topRight.positionOnObstacle = PositionConstants.NORTH_EAST;
+    bottomLeft = new Vertex(x, y + height - 1, this);
+    bottomLeft.positionOnObstacle = PositionConstants.SOUTH_WEST;
+    bottomRight = new Vertex(x + width - 1, y + height - 1, this);
+    bottomRight.positionOnObstacle = PositionConstants.SOUTH_EAST;
+    center = new Vertex(getCenter(), this);
+}
+
+/**
+ * Requests a full reset on all four vertices of this obstacle.
+ */
+void reset() {
+    topLeft.fullReset();
+    bottomLeft.fullReset();
+    bottomRight.fullReset();
+    topRight.fullReset();
+}
+
+private void shrinkVertex(Vertex vertex) {
+    if (vertex.totalCount > 0)
+        vertex.shrink();
+}
+
+/**
+ * Shrinks all four vertices of this obstacle.
+ */
+void shrinkVertices() {
+    shrinkVertex(topLeft);
+    shrinkVertex(topRight);
+    shrinkVertex(bottomLeft);
+    shrinkVertex(bottomRight);
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Rectangle#toString()
+ */
+public String toString() {
+    return Format("Obstacle({}, {}, {}, {})", x, y, //$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$
+        width, height );//$NON-NLS-2$//$NON-NLS-1$
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/Path.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,838 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.Path;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.PositionConstants;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.graph.Segment;
+import dwtx.draw2d.graph.Vertex;
+import dwtx.draw2d.graph.Obstacle;
+
+/**
+ * A Path representation for the ShortestPathRouting. A Path has a start and end point
+ * and may have bendpoints. The output of a path is accessed via the method
+ * <code>getPoints()</code>.
+ *
+ * This class is for internal use only.
+ * @author Whitney Sorenson
+ * @since 3.0
+ */
+public class Path {
+
+/**
+ * A Stack of segments.
+ */
+private static class SegmentStack : ArrayList {
+
+Segment pop() {
+    return cast(Segment)remove(size() - 1);
+}
+
+Obstacle popObstacle() {
+    return cast(Obstacle)remove(size() - 1);
+}
+
+void push(Object obj) {
+    add(obj);
+}
+
+}
+
+private static const Point CURRENT;
+private static const double EPSILON = 1.04;
+private static const Point NEXT;
+private static const double OVAL_CONSTANT = 1.13;
+
+static this(){
+    CURRENT = new Point();
+    NEXT = new Point();
+}
+
+/**
+ * The bendpoint constraints.  The path must go through these bendpoints.
+ */
+PointList bendpoints;
+/**
+ * An arbitrary data field which can be used to map a Path back to some client object.
+ */
+public Object data;
+List excludedObstacles;
+List grownSegments;
+/**
+ * this field is for internal use only.  It is true whenever a property has been changed
+ * which requires the solver to resolve this path.
+ */
+public bool isDirty = true;
+
+bool isInverted = false;
+bool isMarked = false;
+PointList points;
+
+/**
+ * The previous cost ratio of the path.  The cost ratio is the actual path length divided
+ * by the length from the start to the end.
+ */
+private double prevCostRatio;
+List segments;
+
+private SegmentStack stack;
+Vertex start, end;
+private Path subPath;
+double threshold;
+Set visibleObstacles;
+Set visibleVertices;
+
+/**
+ * Constructs a new path.
+ * @since 3.0
+ */
+public this() {
+    segments = new ArrayList();
+    grownSegments = new ArrayList();
+    points = new PointList();
+    visibleVertices = new HashSet();
+    stack = new SegmentStack();
+    visibleObstacles = new HashSet();
+    excludedObstacles = new ArrayList();
+}
+
+/**
+ * Constructs a new path with the given data.
+ * @since 3.0
+ * @param data an arbitrary data field
+ */
+public this(Object data) {
+    this();
+    this.data = data;
+}
+
+/**
+ * Constructs a new path with the given data, start and end point.
+ *
+ * @param start the start point for this path
+ * @param end the end point for this path
+ */
+public this(Point start, Point end) {
+    this(new Vertex(start, null), new Vertex(end, null));
+}
+
+/**
+ * Creates a path between the given vertices.
+ *
+ * @param start start vertex
+ * @param end end vertex
+ */
+this(Vertex start, Vertex end) {
+    this();
+    this.start = start;
+    this.end = end;
+}
+
+/**
+ * Attempts to add all segments between the given obstacles to the visibility graph.
+ * @param source the source obstacle
+ * @param target the target obstacle
+ */
+private void addAllSegmentsBetween(Obstacle source, Obstacle target) {
+    addConnectingSegment(new Segment(source.bottomLeft, target.bottomLeft),
+            source, target, false, false);
+    addConnectingSegment(new Segment(source.bottomRight, target.bottomRight),
+            source, target, true, true);
+    addConnectingSegment(new Segment(source.topLeft, target.topLeft),
+            source, target, true, true);
+    addConnectingSegment(new Segment(source.topRight, target.topRight),
+            source, target, false, false);
+
+    if (source.bottom() is target.bottom()) {
+        addConnectingSegment(new Segment(source.bottomLeft, target.bottomRight),
+                source, target, false, true);
+        addConnectingSegment(new Segment(source.bottomRight, target.bottomLeft),
+                source, target, true, false);
+    }
+    if (source.y is target.y) {
+        addConnectingSegment(new Segment(source.topLeft, target.topRight),
+                source, target, true, false);
+        addConnectingSegment(new Segment(source.topRight, target.topLeft),
+                source, target, false, true);
+    }
+    if (source.x is target.x) {
+        addConnectingSegment(new Segment(source.bottomLeft, target.topLeft),
+                source, target, false, true);
+        addConnectingSegment(new Segment(source.topLeft, target.bottomLeft),
+                source, target, true, false);
+    }
+    if (source.right() is target.right()) {
+        addConnectingSegment(new Segment(source.bottomRight, target.topRight),
+                source, target, true, false);
+        addConnectingSegment(new Segment(source.topRight, target.bottomRight),
+                source, target, false, true);
+    }
+}
+
+/**
+ * Attempts to add a segment between the given obstacles to the visibility graph. This
+ * method is specifically written for the case where the two obstacles intersect and contains
+ * a bool as to whether to check the diagonal that includes the top right point of the
+ * other obstacle.
+ *
+ * @param segment the segment to check
+ * @param o1 the first obstacle
+ * @param o2 the second obstacle
+ * @param checkTopRight1 whether or not to check the diagonal containing top right point
+ */
+private void addConnectingSegment(Segment segment, Obstacle o1, Obstacle o2,
+        bool checkTopRight1, bool checkTopRight2) {
+    if (threshold !is 0
+            && (segment.end.getDistance(end) + segment.end.getDistance(start) > threshold
+                || segment.start.getDistance(end) + segment.start.getDistance(start) > threshold))
+        return;
+
+    if (o2.containsProper(segment.start) || o1.containsProper(segment.end))
+        return;
+
+    if (checkTopRight1 && segment.intersects(o1.x, o1.bottom() - 1, o1.right() - 1, o1.y))
+        return;
+    if (checkTopRight2 && segment.intersects(o2.x, o2.bottom() - 1, o2.right() - 1, o2.y))
+        return;
+    if (!checkTopRight1 && segment.intersects(o1.x, o1.y, o1.right() - 1, o1.bottom() - 1))
+        return;
+    if (!checkTopRight2 && segment.intersects(o2.x, o2.y, o2.right() - 1, o2.bottom() - 1))
+        return;
+
+    stack.push(o1);
+    stack.push(o2);
+    stack.push(segment);
+}
+
+/**
+ * Adds an obstacle to the visibility graph and generates new segments
+ * @param newObs the new obstacle, should not be in the graph already
+ */
+private void addObstacle(Obstacle newObs) {
+    visibleObstacles.add(newObs);
+    Iterator oItr = (new HashSet(visibleObstacles)).iterator();
+    while (oItr.hasNext()) {
+        Obstacle currObs = cast(Obstacle)oItr.next();
+        if (newObs !is currObs)
+            addSegmentsFor(newObs, currObs);
+    }
+    addPerimiterSegments(newObs);
+    addSegmentsFor(start, newObs);
+    addSegmentsFor(end, newObs);
+}
+
+/**
+ * Adds the segments along the perimiter of an obstacle to the visiblity graph queue.
+ * @param obs the obstacle
+ */
+private void addPerimiterSegments(Obstacle obs) {
+    Segment seg = new Segment(obs.topLeft, obs.topRight);
+    stack.push(obs);
+    stack.push(null);
+    stack.push(seg);
+    seg = new Segment(obs.topRight, obs.bottomRight);
+    stack.push(obs);
+    stack.push(null);
+    stack.push(seg);
+    seg = new Segment(obs.bottomRight, obs.bottomLeft);
+    stack.push(obs);
+    stack.push(null);
+    stack.push(seg);
+    seg = new Segment(obs.bottomLeft, obs.topLeft);
+    stack.push(obs);
+    stack.push(null);
+    stack.push(seg);
+}
+
+/**
+ * Attempts to add a segment to the visibility graph.
+ * First checks to see if the segment is outside the threshold oval. Then it compares the segment
+ * against all obstacles. If it is clean, the segment is finally added to the graph.
+ *
+ * @param segment the segment
+ * @param exclude1 an obstacle to exclude from the search
+ * @param exclude2 another obstacle to exclude from the search
+ * @param allObstacles the list of all obstacles
+ */
+private void addSegment(Segment segment, Obstacle exclude1, Obstacle exclude2, List allObstacles) {
+    if (threshold !is 0
+            && (segment.end.getDistance(end) + segment.end.getDistance(start) > threshold
+                || segment.start.getDistance(end) + segment.start.getDistance(start) > threshold))
+        return;
+
+    for (int i = 0; i < allObstacles.size(); i++) {
+        Obstacle obs = cast(Obstacle)allObstacles.get(i);
+
+        if (obs is exclude1 || obs is exclude2 || obs.exclude)
+            continue;
+
+        if (segment.intersects(obs.x, obs.y, obs.right() - 1, obs.bottom() - 1)
+                || segment.intersects(obs.x, obs.bottom() - 1, obs.right() - 1, obs.y)
+                || obs.containsProper(segment.start)
+                || obs.containsProper(segment.end)) {
+            if (!visibleObstacles.contains(obs))
+                addObstacle(obs);
+            return;
+        }
+    }
+
+    linkVertices(segment);
+}
+
+/**
+ * Adds the segments between the given obstacles.
+ * @param source source obstacle
+ * @param target target obstacle
+ */
+private void addSegmentsFor(Obstacle source, Obstacle target) {
+    if (source.intersects(target))
+        addAllSegmentsBetween(source, target);
+    else if (target.bottom() - 1 < source.y)
+        addSegmentsTargetAboveSource(source, target);
+    else if (source.bottom() - 1 < target.y)
+        addSegmentsTargetAboveSource(target, source);
+    else if (target.right() - 1 < source.x)
+        addSegmentsTargetBesideSource(source, target);
+    else
+        addSegmentsTargetBesideSource(target, source);
+}
+
+/**
+ * Adds the segments between the given obstacles.
+ * @param source source obstacle
+ * @param target target obstacle
+ */
+private void addSegmentsFor(Vertex vertex, Obstacle obs) {
+    Segment seg = null;
+    Segment seg2 = null;
+
+    switch (obs.getPosition(vertex)) {
+        case PositionConstants.SOUTH_WEST :
+        case PositionConstants.NORTH_EAST :
+            seg = new Segment(vertex, obs.topLeft);
+            seg2 = new Segment(vertex, obs.bottomRight);
+            break;
+        case PositionConstants.SOUTH_EAST :
+        case PositionConstants.NORTH_WEST :
+            seg = new Segment(vertex, obs.topRight);
+            seg2 = new Segment(vertex, obs.bottomLeft);
+            break;
+        case PositionConstants.NORTH :
+            seg = new Segment(vertex, obs.topLeft);
+            seg2 = new Segment(vertex, obs.topRight);
+            break;
+        case PositionConstants.EAST :
+            seg = new Segment(vertex, obs.bottomRight);
+            seg2 = new Segment(vertex, obs.topRight);
+            break;
+        case PositionConstants.SOUTH :
+            seg = new Segment(vertex, obs.bottomRight);
+            seg2 = new Segment(vertex, obs.bottomLeft);
+            break;
+        case PositionConstants.WEST :
+            seg = new Segment(vertex, obs.topLeft);
+            seg2 = new Segment(vertex, obs.bottomLeft);
+            break;
+        default:
+            if (vertex.x is obs.x) {
+                seg = new Segment(vertex, obs.topLeft);
+                seg2 = new Segment(vertex, obs.bottomLeft);
+            } else if (vertex.y is obs.y) {
+                seg = new Segment(vertex, obs.topLeft);
+                seg2 = new Segment(vertex, obs.topRight);
+            } else if (vertex.y is obs.bottom() - 1) {
+                seg = new Segment(vertex, obs.bottomLeft);
+                seg2 = new Segment(vertex, obs.bottomRight);
+            } else if (vertex.x is obs.right() - 1) {
+                seg = new Segment(vertex, obs.topRight);
+                seg2 = new Segment(vertex, obs.bottomRight);
+            } else {
+                throw new RuntimeException("Unexpected vertex conditions"); //$NON-NLS-1$
+            }
+    }
+
+    stack.push(obs);
+    stack.push(null);
+    stack.push(seg);
+    stack.push(obs);
+    stack.push(null);
+    stack.push(seg2);
+}
+
+private void addSegmentsTargetAboveSource(Obstacle source, Obstacle target) {
+    //target located above source
+    Segment seg = null;
+    Segment seg2 = null;
+    if (target.x > source.x) {
+        seg = new Segment(source.topLeft, target.topLeft);
+        if (target.x < source.right() - 1)
+            seg2 = new Segment(source.topRight, target.bottomLeft);
+        else
+            seg2 = new Segment(source.bottomRight, target.topLeft);
+    } else if (source.x is target.x) {
+        seg = new Segment(source.topLeft, target.bottomLeft);
+        seg2 = new Segment(source.topRight, target.bottomLeft);
+    } else {
+        seg = new Segment(source.bottomLeft, target.bottomLeft);
+        seg2 = new Segment(source.topRight, target.bottomLeft);
+    }
+
+    stack.push(source);
+    stack.push(target);
+    stack.push(seg);
+    stack.push(source);
+    stack.push(target);
+    stack.push(seg2);
+    seg = null;
+    seg2 = null;
+
+    if (target.right() < source.right()) {
+        seg = new Segment(source.topRight, target.topRight);
+        if (target.right() - 1 > source.x)
+            seg2 = new Segment(source.topLeft, target.bottomRight);
+        else
+            seg2 = new Segment(source.bottomLeft, target.topRight);
+    } else if (source.right() is target.right()) {
+        seg = new Segment(source.topRight, target.bottomRight);
+        seg2 = new Segment(source.topLeft, target.bottomRight);
+    } else {
+        seg = new Segment(source.bottomRight, target.bottomRight);
+        seg2 = new Segment(source.topLeft, target.bottomRight);
+    }
+
+    stack.push(source);
+    stack.push(target);
+    stack.push(seg);
+    stack.push(source);
+    stack.push(target);
+    stack.push(seg2);
+}
+
+private void addSegmentsTargetBesideSource(Obstacle source, Obstacle target) {
+    //target located above source
+    Segment seg = null;
+    Segment seg2 = null;
+    if (target.y > source.y) {
+        seg = new Segment(source.topLeft, target.topLeft);
+        if (target.y < source.bottom() - 1)
+            seg2 = new Segment(source.bottomLeft, target.topRight);
+        else
+            seg2 = new Segment(source.bottomRight, target.topLeft);
+    } else if (source.y is target.y) {
+        //degenerate case
+        seg = new Segment(source.topLeft, target.topRight);
+        seg2 = new Segment(source.bottomLeft, target.topRight);
+    } else {
+        seg = new Segment(source.topRight, target.topRight);
+        seg2 = new Segment(source.bottomLeft, target.topRight);
+    }
+    stack.push(source);
+    stack.push(target);
+    stack.push(seg);
+    stack.push(source);
+    stack.push(target);
+    stack.push(seg2);
+    seg = null;
+    seg2 = null;
+
+    if (target.bottom() < source.bottom()) {
+        seg = new Segment(source.bottomLeft, target.bottomLeft);
+        if (target.bottom() - 1 > source.y)
+            seg2 = new Segment(source.topLeft, target.bottomRight);
+        else
+            seg2 = new Segment(source.topRight, target.bottomLeft);
+    } else if (source.bottom() is target.bottom()) {
+        seg = new Segment(source.bottomLeft, target.bottomRight);
+        seg2 = new Segment(source.topLeft, target.bottomRight);
+    } else {
+        seg = new Segment(source.bottomRight, target.bottomRight);
+        seg2 = new Segment(source.topLeft, target.bottomRight);
+    }
+    stack.push(source);
+    stack.push(target);
+    stack.push(seg);
+    stack.push(source);
+    stack.push(target);
+    stack.push(seg2);
+}
+
+/**
+ *
+ */
+void cleanup() {
+    //segments.clear();
+    visibleVertices.clear();
+}
+
+/**
+ * Begins the creation of the visibility graph with the first segment
+ * @param allObstacles list of all obstacles
+ */
+private void createVisibilityGraph(List allObstacles) {
+    stack.push(null);
+    stack.push(null);
+    stack.push(new Segment(start, end));
+
+    while (!stack.isEmpty())
+        addSegment(stack.pop(), stack.popObstacle(), stack.popObstacle(), allObstacles);
+}
+
+/**
+ * Once the visibility graph is constructed, this is called to label the graph and
+ * determine the shortest path. Returns false if no path can be found.
+ *
+ * @return true if a path can be found.
+ */
+private bool determineShortestPath() {
+    if (!labelGraph())
+        return false;
+    Vertex vertex = end;
+    prevCostRatio = end.cost / start.getDistance(end);
+
+    Vertex nextVertex;
+    while (!vertex.opEquals(start)) {
+        nextVertex = vertex.label;
+        if (nextVertex is null)
+            return false;
+        Segment s = new Segment(nextVertex, vertex);
+        segments.add(s);
+        vertex = nextVertex;
+    }
+
+    Collections.reverse(segments);
+    return true;
+}
+
+/**
+ * Resets all necessary fields for a solve.
+ */
+void fullReset() {
+    visibleVertices.clear();
+    segments.clear();
+    if (prevCostRatio is 0) {
+        double distance = start.getDistance(end);
+        threshold = distance * OVAL_CONSTANT;
+    } else
+        threshold = prevCostRatio * EPSILON * start.getDistance(end);
+    visibleObstacles.clear();
+    resetPartial();
+}
+
+/**
+ * Creates the visibility graph and returns whether or not a shortest path could be
+ * determined.
+ *
+ * @param allObstacles the list of all obstacles
+ * @return true if a shortest path was found
+ */
+bool generateShortestPath(List allObstacles) {
+    createVisibilityGraph(allObstacles);
+
+    if (visibleVertices.size() is 0)
+        return false;
+
+    return determineShortestPath();
+}
+
+/**
+ * Returns the list of constrained points through which this path must pass or
+ * <code>null</code>.
+ * @see #setBendPoints(PointList)
+ * @return list of bend points
+ */
+public PointList getBendPoints() {
+    return bendpoints;
+}
+
+/**
+ * Returns the end point for this path
+ * @return end point for this path
+ */
+public Point getEndPoint() {
+    return end;
+}
+
+/**
+ * Returns the solution to this path.
+ *
+ * @return the points for this path.
+ */
+public PointList getPoints() {
+    return points;
+}
+
+/**
+ * Returns the start point for this path
+ * @return start point for this path
+ */
+public Point getStartPoint() {
+    return start;
+}
+
+/**
+ * Returns a subpath for this path at the given segment
+ *
+ * @param currentSegment the segment at which the subpath should be created
+ * @return the new path
+ */
+Path getSubPath(Segment currentSegment) {
+    // ready new path
+    Path newPath = new Path(currentSegment.start, end);
+    newPath.grownSegments = new ArrayList(grownSegments.subList(
+            grownSegments.indexOf(currentSegment),
+            grownSegments.size()));
+
+    // fix old path
+    grownSegments = new ArrayList(grownSegments.subList(
+            0, grownSegments.indexOf(currentSegment) + 1));
+    end = currentSegment.end;
+
+    subPath = newPath;
+    return newPath;
+}
+
+/**
+ * Resets the vertices that this path has traveled prior to this segment. This is called
+ * when the path has become inverted and needs to rectify any labeling mistakes it made
+ * before it knew it was inverted.
+ * @param currentSegment the segment at which the path found it was inverted
+ */
+void invertPriorVertices(Segment currentSegment) {
+    int stop = grownSegments.indexOf(currentSegment);
+    for (int i = 0; i < stop; i++) {
+        Vertex vertex = (cast(Segment)grownSegments.get(i)).end;
+        if (vertex.type is Vertex.INNIE)
+            vertex.type = Vertex.OUTIE;
+        else
+            vertex.type = Vertex.INNIE;
+    }
+}
+
+/**
+ * Returns true if this obstacle is in the visibility graph
+ * @param obs the obstacle
+ * @return true if obstacle is in the visibility graph
+ */
+bool isObstacleVisible(Obstacle obs) {
+    return visibleObstacles.contains(obs);
+}
+
+/**
+ * Labels the visibility graph to assist in finding the shortest path
+ * @return false if there was a gap in the visibility graph
+ */
+private bool labelGraph() {
+    int numPermanentNodes = 1;
+    Vertex vertex = start;
+    Vertex neighborVertex = null;
+    vertex.isPermanent = true;
+    double newCost;
+    while (numPermanentNodes !is visibleVertices.size()) {
+        List neighbors = vertex.neighbors;
+        if (neighbors is null)
+            return false;
+        // label neighbors if they have a new shortest path
+        for (int i = 0; i < neighbors.size(); i++) {
+            neighborVertex = cast(Vertex)neighbors.get(i);
+            if (!neighborVertex.isPermanent) {
+                newCost = vertex.cost + vertex.getDistance(neighborVertex);
+                if (neighborVertex.label is null) {
+                    neighborVertex.label = vertex;
+                    neighborVertex.cost = newCost;
+                } else if (neighborVertex.cost > newCost) {
+                    neighborVertex.label = vertex;
+                    neighborVertex.cost = newCost;
+                }
+            }
+        }
+        // find the next none-permanent, labeled vertex with smallest cost
+        double smallestCost = 0;
+        Vertex tempVertex = null;
+        Iterator v = visibleVertices.iterator();
+        while (v.hasNext()) {
+            tempVertex = cast(Vertex)v.next();
+            if (!tempVertex.isPermanent && tempVertex.label !is null
+                    && (tempVertex.cost < smallestCost || smallestCost is 0)) {
+                smallestCost = tempVertex.cost;
+                vertex = tempVertex;
+            }
+        }
+        // set the new vertex to permanent.
+        vertex.isPermanent = true;
+        numPermanentNodes++;
+    }
+    return true;
+}
+
+/**
+ * Links two vertices together in the visibility graph
+ * @param segment the segment to add
+ */
+private void linkVertices(Segment segment) {
+    if (segment.start.neighbors is null)
+        segment.start.neighbors = new ArrayList();
+    if (segment.end.neighbors is null)
+        segment.end.neighbors = new ArrayList();
+
+    if (!segment.start.neighbors.contains(segment.end)) {
+        segment.start.neighbors.add(segment.end);
+        segment.end.neighbors.add(segment.start);
+    }
+
+    visibleVertices.add(segment.start);
+    visibleVertices.add(segment.end);
+}
+
+/**
+ * Called to reconnect a subpath back onto this path. Does a depth-first search to
+ * reconnect all paths. Should be called after sorting.
+ */
+void reconnectSubPaths() {
+    if (subPath !is null) {
+        subPath.reconnectSubPaths();
+
+        Segment changedSegment = cast(Segment)subPath.grownSegments.remove(0);
+        Segment oldSegment = cast(Segment)grownSegments.get(grownSegments.size() - 1);
+
+        oldSegment.end = changedSegment.end;
+        grownSegments.addAll(subPath.grownSegments);
+
+        subPath.points.removePoint(0);
+        points.removePoint(points.size() - 1);
+        points.addAll(subPath.points);
+
+        visibleObstacles.addAll(subPath.visibleObstacles);
+
+        end = subPath.end;
+        subPath = null;
+    }
+}
+
+
+/**
+ * Refreshes the exclude field on the obstacles in the list. Excludes all obstacles
+ * that contain the start or end point for this path.
+ * @param allObstacles list of all obstacles
+ */
+void refreshExcludedObstacles(List allObstacles) {
+    excludedObstacles.clear();
+
+    for (int i = 0; i < allObstacles.size(); i++) {
+        Obstacle o = cast(Obstacle)allObstacles.get(i);
+        o.exclude = false;
+
+        if (o.contains(start)) {
+            if (o.containsProper(start))
+                o.exclude = true;
+            else {
+                /*
+                 * $TODO Check for corners.  If the path begins exactly at the corner of
+                 * an obstacle, the exclude should also be true.
+                 *
+                 * Or, change segment intersection so that two segments that share an
+                 * endpoint do not intersect.
+                 */
+            }
+        }
+
+        if (o.contains(end)) {
+            if (o.containsProper(end))
+                o.exclude = true;
+            else {
+                //check for corners.  See above statement.
+            }
+        }
+
+        if (o.exclude && !excludedObstacles.contains(o))
+            excludedObstacles.add(o);
+    }
+}
+
+/**
+ * Resets the fields for everything in the solve after the visibility graph steps.
+ */
+void resetPartial() {
+    isMarked = false;
+    isInverted = false;
+    subPath = null;
+    isDirty = false;
+    grownSegments.clear();
+    points.removeAllPoints();
+}
+
+/**
+ * Sets the list of bend points to the given list and dirties the path.
+ * @param bendPoints the list of bend points
+ */
+public void setBendPoints(PointList bendPoints) {
+    this.bendpoints = bendPoints;
+    isDirty = true;
+}
+
+/**
+ * Sets the end point for this path to the given point.
+ * @param end the new end point for this path
+ */
+public void setEndPoint(Point end) {
+    if (end.opEquals(this.end))
+        return;
+    this.end = new Vertex(end, null);
+    isDirty = true;
+}
+
+/**
+ * Sets the start point for this path to the given point.
+ * @param start the new start point for this path
+ */
+public void setStartPoint(Point start) {
+    if (start.opEquals(this.start))
+        return;
+    this.start = new Vertex(start, null);
+    isDirty = true;
+}
+
+/**
+ * Returns <code>true</code> if the path is clean and intersects the given obstacle. Also
+ * dirties the path in the process.
+ * @since 3.0
+ * @param obs the obstacle
+ * @return <code>true</code> if a clean path touches the obstacle
+ */
+ bool testAndSet(Obstacle obs) {
+    if (isDirty)
+        return false;
+    //This will never actually happen because obstacles are not stored by identity
+    if (excludedObstacles.contains(obs))
+        return false;
+
+    Segment seg1 = new Segment(obs.topLeft, obs.bottomRight);
+    Segment seg2 = new Segment(obs.topRight, obs.bottomLeft);
+
+    for (int s = 0; s < points.size() - 1; s++) {
+        points.getPoint(CURRENT, s);
+        points.getPoint(NEXT, s + 1);
+
+        if (seg1.intersects(CURRENT, NEXT) || seg2.intersects(CURRENT, NEXT)
+                || obs.contains(CURRENT) || obs.contains(NEXT)) {
+            isDirty = true;
+            return true;
+        }
+    }
+    return false;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/PopulateRanks.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.PopulateRanks;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.draw2d.graph.RevertableChange;
+import dwtx.draw2d.graph.GraphVisitor;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.RankList;
+import dwtx.draw2d.graph.Rank;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.Edge;
+import dwtx.draw2d.graph.VirtualNodeCreation;
+
+/**
+ * This class takes a DirectedGraph with an optimal rank assignment and a spanning tree,
+ * and populates the ranks of the DirectedGraph. Virtual nodes are inserted for edges that
+ * span 1 or more ranks.
+ * <P>
+ * Ranks are populated using a pre-order depth-first traversal of the spanning tree. For
+ * each node, all edges requiring virtual nodes are added to the ranks.
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+class PopulateRanks : GraphVisitor {
+
+private Stack changes;
+
+public this(){
+    changes = new Stack();
+}
+/**
+ * @see GraphVisitor#visit(DirectedGraph)
+ */
+public void visit(DirectedGraph g) {
+    if (g.forestRoot !is null) {
+        for (int i = g.forestRoot.outgoing.size() - 1; i >= 0; i--)
+            g.removeEdge(g.forestRoot.outgoing.getEdge(i));
+        g.removeNode(g.forestRoot);
+    }
+    g.ranks = new RankList();
+    for (int i = 0; i < g.nodes.size(); i++) {
+        Node node = g.nodes.getNode(i);
+        g.ranks.getRank(node.rank).add(node);
+    }
+    for (int i = 0; i < g.nodes.size(); i++) {
+        Node node = g.nodes.getNode(i);
+        for (int j = 0; j < node.outgoing.size();) {
+            Edge e = node.outgoing.getEdge(j);
+            if (e.getLength() > 1)
+                changes.push(new VirtualNodeCreation(e, g));
+            else
+                j++;
+        }
+    }
+}
+
+/**
+ * @see GraphVisitor#revisit(DirectedGraph)
+ */
+public void revisit(DirectedGraph g) {
+    for (int r = 0; r < g.ranks.size(); r++) {
+        Rank rank = g.ranks.getRank(r);
+        Node prev = null, cur;
+        for (int n = 0; n < rank.size(); n++) {
+            cur = rank.getNode(n);
+            cur.left = prev;
+            if (prev !is null) {
+                prev.right = cur;
+            }
+            prev = cur;
+        }
+    }
+    for (int i = 0; i < changes.size(); i++) {
+        RevertableChange change = cast(RevertableChange)changes.get(i);
+        change.revert();
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/Rank.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.Rank;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.SubgraphBoundary;
+import dwtx.draw2d.graph.NodeList;
+
+/**
+ * For Internal Use only.
+ * @author hudsonr
+ * @since 2.1.2
+ */
+public class Rank : NodeList {
+
+alias NodeList.add add;
+
+int bottomPadding;
+int height;
+int location;
+
+const int hash;
+int topPadding;
+int total;
+
+public this(){
+    hash = (new Object()).toHash();
+}
+
+void add(Node n) {
+    super.add(n);
+}
+
+void assignIndices() {
+    total = 0;
+    Node node;
+
+    int mag;
+    for (int i = 0; i < size(); i++) {
+        node = getNode(i);
+        mag = Math.max(1, node.incoming.size() + node.outgoing.size());
+        mag = Math.min(mag, 5);
+        if (null !is cast(SubgraphBoundary)node )
+            mag = 4;
+        total += mag;
+        node.index = total;
+        total += mag;
+    }
+}
+
+/**
+ * Returns the number of nodes in this rank.
+ * @return the number of nodes
+ */
+public int count() {
+    return super.size();
+}
+
+/**
+ * @see Object#equals(Object)
+ */
+public override int opEquals(Object o) {
+    return o is this;
+}
+
+/**
+ * @see Object#toHash()
+ * Overridden for speed based on equality.
+ */
+public override hash_t toHash() {
+    return hash;
+}
+
+void setDimensions(int location, int rowHeight) {
+    this.height = rowHeight;
+    this.location = location;
+    for (int i = 0; i < size(); i++) {
+        Node n = getNode(i);
+        n.y = location;
+        n.height = rowHeight;
+    }
+}
+
+/**
+ * @deprecated Do not call
+ */
+public void sort() { }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/RankAssignmentSolver.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,322 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.RankAssignmentSolver;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.draw2d.graph.SpanningTreeVisitor;
+import dwtx.draw2d.graph.EdgeList;
+import dwtx.draw2d.graph.Edge;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.NodeList;
+
+/**
+ * Assigns the final rank assignment for a DirectedGraph with an initial feasible
+ * spanning tree.
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+class RankAssignmentSolver : SpanningTreeVisitor {
+
+DirectedGraph graph;
+EdgeList spanningTree;
+bool searchDirection;
+
+int depthFirstCutValue(Edge edge, int count) {
+    Node n = getTreeTail(edge);
+    setTreeMin(n, count);
+    int cutvalue = 0;
+    int multiplier = (edge.target is n) ? 1 : -1;
+    EdgeList list;
+
+    list = n.outgoing;
+    Edge e;
+    for (int i = 0; i < list.size(); i++) {
+        e = list.getEdge(i);
+        if (e.tree && e !is edge) {
+            count = depthFirstCutValue(e, count);
+            cutvalue += (e.cut - e.weight) * multiplier;
+        } else {
+            cutvalue -= e.weight * multiplier;
+        }
+    }
+    list = n.incoming;
+    for (int i = 0; i < list.size(); i++) {
+        e = list.getEdge(i);
+        if (e.tree && e !is edge) {
+            count = depthFirstCutValue(e, count);
+            cutvalue -= (e.cut - e.weight) * multiplier;
+        } else {
+            cutvalue += e.weight * multiplier;
+        }
+    }
+
+    edge.cut = cutvalue;
+    if (cutvalue < 0)
+        spanningTree.add(edge);
+    setTreeMax(n, count);
+    return count + 1;
+}
+
+/**
+ * returns the Edge which should be entered.
+ * @param branch
+ * @return Edge
+ */
+Edge enter(Node branch) {
+    Node n;
+    Edge result = null;
+    int minSlack = Integer.MAX_VALUE;
+    bool incoming = getParentEdge(branch).target !is branch;
+//  searchDirection = !searchDirection;
+    for (int i = 0; i < graph.nodes.size(); i++) {
+        if (searchDirection)
+            n = graph.nodes.getNode(i);
+        else
+            n = graph.nodes.getNode(graph.nodes.size() - 1 - i);
+        if (subtreeContains(branch, n)) {
+            EdgeList edges;
+            if (incoming)
+                edges = n.incoming;
+            else
+                edges = n.outgoing;
+            for (int j = 0; j < edges.size(); j++) {
+                Edge e = edges.getEdge(j);
+                if (!subtreeContains(branch, e.opposite(n))
+                  && !e.tree
+                  && e.getSlack() < minSlack) {
+                    result = e;
+                    minSlack = e.getSlack();
+                }
+            }
+        }
+    }
+    return result;
+}
+
+int getTreeMax(Node n) {
+    return n.workingInts[1];
+}
+
+int getTreeMin(Node n) {
+    return n.workingInts[0];
+}
+
+void initCutValues() {
+    Node root = graph.nodes.getNode(0);
+    spanningTree = new EdgeList();
+    Edge e;
+    setTreeMin(root, 1);
+    setTreeMax(root, 1);
+
+    for (int i = 0; i < root.outgoing.size(); i++) {
+        e = root.outgoing.getEdge(i);
+        if (!getSpanningTreeChildren(root).contains(e))
+            continue;
+        setTreeMax(root, depthFirstCutValue(e, getTreeMax(root)));
+    }
+    for (int i = 0; i < root.incoming.size(); i++) {
+        e = root.incoming.getEdge(i);
+        if (!getSpanningTreeChildren(root).contains(e))
+            continue;
+        setTreeMax(root, depthFirstCutValue(e, getTreeMax(root)));
+    }
+}
+
+Edge leave() {
+    Edge result = null;
+    Edge e;
+    int minCut = 0;
+    int weight = -1;
+    for (int i = 0; i < spanningTree.size(); i++) {
+        e = spanningTree.getEdge(i);
+        if (e.cut < minCut) {
+            result = e;
+            minCut = result.cut;
+            weight = result.weight;
+        } else if (e.cut is minCut && e.weight > weight) {
+            result = e;
+            weight = result.weight;
+        }
+    }
+    return result;
+}
+
+void networkSimplexLoop() {
+    Edge leave_, enter_;
+    int count = 0;
+    while ((leave_ = leave()) !is null && count < 900) {
+
+        count++;
+
+        Node leaveTail = getTreeTail(leave_);
+        Node leaveHead = getTreeHead(leave_);
+
+        enter_ = enter(leaveTail);
+        if (enter_ is null)
+            break;
+
+        //Break the "leave" edge from the spanning tree
+        getSpanningTreeChildren(leaveHead).remove(leave_);
+        setParentEdge(leaveTail, null);
+        leave_.tree = false;
+        spanningTree.remove(leave_);
+
+        Node enterTail = enter_.source;
+        if (!subtreeContains(leaveTail, enterTail))
+            //Oops, wrong end of the edge
+            enterTail = enter_.target;
+        Node enterHead = enter_.opposite(enterTail);
+
+        //Prepare enterTail by making it the root of its sub-tree
+        updateSubgraph(enterTail);
+
+        //Add "enter" edge to the spanning tree
+        getSpanningTreeChildren(enterHead).add(enter_);
+        setParentEdge(enterTail, enter_);
+        enter_.tree = true;
+
+        repairCutValues(enter_);
+
+        Node commonAncestor = enterHead;
+
+        while (!subtreeContains(commonAncestor, leaveHead)) {
+            repairCutValues(getParentEdge(commonAncestor));
+            commonAncestor = getTreeParent(commonAncestor);
+        }
+        while (leaveHead !is commonAncestor) {
+            repairCutValues(getParentEdge(leaveHead));
+            leaveHead = getTreeParent(leaveHead);
+        }
+        updateMinMax(commonAncestor, getTreeMin(commonAncestor));
+        tightenEdge(enter_);
+    }
+}
+
+void repairCutValues(Edge edge) {
+    spanningTree.remove(edge);
+    Node n = getTreeTail(edge);
+    int cutvalue = 0;
+    int multiplier = (edge.target is n) ? 1 : -1;
+    EdgeList list;
+
+    list = n.outgoing;
+    Edge e;
+    for (int i = 0; i < list.size(); i++) {
+        e = list.getEdge(i);
+        if (e.tree && e !is edge)
+            cutvalue += (e.cut - e.weight) * multiplier;
+        else
+            cutvalue -= e.weight * multiplier;
+    }
+    list = n.incoming;
+    for (int i = 0; i < list.size(); i++) {
+        e = list.getEdge(i);
+        if (e.tree && e !is edge)
+            cutvalue -= (e.cut - e.weight) * multiplier;
+        else
+            cutvalue += e.weight * multiplier;
+    }
+
+    edge.cut = cutvalue;
+    if (cutvalue < 0)
+        spanningTree.add(edge);
+}
+
+void setTreeMax(Node n, int value) {
+    n.workingInts[1] = value;
+}
+
+void setTreeMin(Node n, int value) {
+    n.workingInts[0] = value;
+}
+
+bool subtreeContains(Node parent, Node child) {
+    return parent.workingInts[0] <= child.workingInts[1]
+      && child.workingInts[1] <= parent.workingInts[1];
+}
+
+void tightenEdge(Edge edge) {
+    Node tail = getTreeTail(edge);
+    int delta = edge.getSlack();
+    if (tail is edge.target)
+        delta = -delta;
+    Node n;
+    for (int i = 0; i < graph.nodes.size(); i++) {
+        n = graph.nodes.getNode(i);
+        if (subtreeContains(tail, n))
+            n.rank += delta;
+    }
+}
+
+int updateMinMax(Node root, int count) {
+    setTreeMin(root, count);
+    EdgeList edges = getSpanningTreeChildren(root);
+    for (int i = 0; i < edges.size(); i++)
+        count = updateMinMax(getTreeTail(edges.getEdge(i)), count);
+    setTreeMax(root, count);
+    return count + 1;
+}
+
+void updateSubgraph(Node root) {
+    Edge flip = getParentEdge(root);
+    if (flip !is null) {
+        Node rootParent = getTreeParent(root);
+        getSpanningTreeChildren(rootParent).remove(flip);
+        updateSubgraph(rootParent);
+        setParentEdge(root, null);
+        setParentEdge(rootParent, flip);
+        repairCutValues(flip);
+        getSpanningTreeChildren(root).add(flip);
+    }
+}
+
+public void visit(DirectedGraph graph) {
+    this.graph = graph;
+    initCutValues();
+    networkSimplexLoop();
+    if (graph.forestRoot is null)
+        graph.nodes.normalizeRanks();
+    else
+        normalizeForest();
+}
+
+private void normalizeForest() {
+    NodeList tree = new NodeList();
+    graph.nodes.resetFlags();
+    graph.forestRoot.flag = true;
+    EdgeList rootEdges = graph.forestRoot.outgoing;
+    Stack stack = new Stack();
+    for (int i = 0; i < rootEdges.size(); i++) {
+        Node node = rootEdges.getEdge(i).target;
+        node.flag = true;
+        stack.push(node);
+        while (!stack.isEmpty()) {
+            node = cast(Node) stack.pop();
+            tree.add(node);
+            Iterator neighbors = node.iteratorNeighbors();
+            while (neighbors.hasNext()) {
+                Node neighbor = cast(Node) neighbors.next();
+                if (!neighbor.flag) {
+                    neighbor.flag = true;
+                    stack.push(neighbor);
+                }
+            }
+        }
+        tree.normalizeRanks();
+        tree.clear();
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/RankList.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.RankList;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.draw2d.graph.Rank;
+
+/**
+ * For internal use only.  A list of lists.
+ * @author hudsonr
+ * @since 2.1.2
+ */
+public final class RankList {
+
+ArrayList ranks;
+
+public this(){
+    ranks = new ArrayList();
+}
+/**
+ * Returns the specified rank.
+ * @param rank the row
+ * @return the rank
+ */
+public Rank getRank(int rank) {
+    while (ranks.size() <= rank)
+        ranks.add(new Rank());
+    return cast(Rank)ranks.get(rank);
+}
+
+/**
+ * Returns the total number or ranks.
+ * @return the total number of ranks
+ */
+public int size() {
+    return ranks.size();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/RankSorter.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,239 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.RankSorter;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.dwtxhelper.Random;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.Rank;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.EdgeList;
+import dwtx.draw2d.graph.Edge;
+import dwtx.draw2d.graph.GraphUtilities;
+
+/**
+ * Sorts Ranks during the up and down sweeps of the MinCross visitor.
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+class RankSorter {
+
+Random flipflop;
+Node node;
+double rankSize, prevRankSize, nextRankSize;
+int currentRow;
+Rank rank;
+double progress;
+DirectedGraph g;
+
+public this(){
+    flipflop = new Random(3);
+}
+
+protected void assignIncomingSortValues() {
+    rankSize = rank.total;
+    prevRankSize = g.ranks.getRank(currentRow - 1).total;
+    if (currentRow < g.ranks.size() - 1)
+        nextRankSize = g.ranks.getRank(currentRow + 1).total;
+    for (int n = 0; n < rank.count(); n++) {
+        node = rank.getNode(n);
+        sortValueIncoming();
+    }
+}
+
+protected void assignOutgoingSortValues() {
+    rankSize = rank.total;
+    prevRankSize = g.ranks.getRank(currentRow + 1).total;
+    if (currentRow > 1)
+        nextRankSize = g.ranks.getRank(currentRow - 1).total;
+
+    for (int n = 0; n < rank.count(); n++) {
+        node = rank.getNode(n);
+        sortValueOutgoing();
+    }
+}
+
+double evaluateNodeIncoming() {
+    bool change = false;
+    EdgeList incoming = node.incoming;
+    do {
+        change = false;
+        for (int i = 0; i < incoming.size() - 1; i++) {
+            if (incoming.getSourceIndex(i) > incoming.getSourceIndex(i + 1)) {
+                Edge e = incoming.getEdge(i);
+                incoming.set(i, incoming.get(i + 1));
+                incoming.set(i + 1, e);
+                change = true;
+            }
+        }
+    } while (change);
+
+    int n = incoming.size();
+    if (n is 0) {
+        return node.index * prevRankSize / rankSize;
+    }
+    if (n % 2 is 1)
+        return incoming.getSourceIndex(n / 2);
+
+    int l = incoming.getSourceIndex(n / 2 - 1);
+    int r = incoming.getSourceIndex(n / 2);
+    if (progress >= 0.8 && n > 2) {
+        int dl = l - incoming.getSourceIndex(0);
+        int dr = incoming.getSourceIndex(n - 1) - r;
+        if (dl < dr)
+            return l;
+        if (dl > dr)
+            return r;
+    }
+    if (progress > 0.25 && progress < 0.75) {
+        if (flipflop.nextBoolean())
+            return (l + l + r) / 3.0;
+        else
+            return (r + r + l) / 3.0;
+    }
+    return (l + r) / 2.0;
+}
+
+double evaluateNodeOutgoing() {
+    bool change = false;
+    EdgeList outgoing = node.outgoing;
+    do {
+        change = false;
+        for (int i = 0; i < outgoing.size() - 1; i++) {
+            if (outgoing.getTargetIndex(i) > outgoing.getTargetIndex(i + 1)) {
+                Edge e = outgoing.getEdge(i);
+                outgoing.set(i, outgoing.get(i + 1));
+                outgoing.set(i + 1, e);
+                change = true;
+            }
+        }
+    } while (change);
+
+    int n = outgoing.size();
+    if (n is 0)
+        return node.index * prevRankSize / rankSize;
+    if (n % 2 is 1)
+        return outgoing.getTargetIndex(n / 2);
+    int l = outgoing.getTargetIndex(n / 2 - 1);
+    int r = outgoing.getTargetIndex(n / 2);
+    if (progress >= 0.8 && n > 2) {
+        int dl = l - outgoing.getTargetIndex(0);
+        int dr = outgoing.getTargetIndex(n - 1) - r;
+        if (dl < dr)
+            return l;
+        if (dl > dr)
+            return r;
+    }
+    if (progress > 0.25 && progress < 0.75) {
+        if (flipflop.nextBoolean())
+            return (l + l + r) / 3.0;
+        else
+            return (r + r + l) / 3.0;
+    }
+    return (l + r) / 2.0;
+}
+
+public void sortRankIncoming(DirectedGraph g, Rank rank, int row, double progress) {
+    this.currentRow = row;
+    this.rank = rank;
+    this.progress = progress;
+    assignIncomingSortValues();
+    sort();
+    postSort();
+}
+
+public void init(DirectedGraph g) {
+    this.g = g;
+    for (int i = 0; i < g.ranks.size(); i++) {
+        rank = g.ranks.getRank(i);
+
+        //Sort the ranks based on their constraints. Constraints are preserved throughout.
+        Collections.sort(rank, new class() Comparator {
+            public int compare(Object left, Object right) {
+                return (cast(Node)left).rowOrder - (cast(Node)right).rowOrder;
+            }
+        });
+        postSort();
+    }
+}
+
+void optimize(DirectedGraph g) {
+}
+
+protected void postSort() {
+    rank.assignIndices();
+}
+
+void sort() {
+    bool change;
+    do {
+        change = false;
+        for (int i = 0; i < rank.size() - 1; i++)
+            change |= swap(i);
+        if (!change)
+            break;
+        change = false;
+        for (int i = rank.size() - 2; i >= 0; i--)
+            change |= swap(i);
+    } while (change);
+}
+
+bool swap(int i) {
+    Node left = rank.getNode(i);
+    Node right = rank.getNode(i + 1);
+    if (GraphUtilities.isConstrained(left, right))
+        return false;
+    if (left.sortValue <= right.sortValue)
+        return false;
+    rank.set(i, right);
+    rank.set(i + 1, left);
+    return true;
+}
+
+public void sortRankOutgoing(DirectedGraph g, Rank rank, int row, double progress) {
+    this.currentRow = row;
+    this.rank = rank;
+    this.progress = progress;
+    assignOutgoingSortValues();
+    sort();
+    postSort();
+}
+
+void sortValueIncoming() {
+    node.sortValue = evaluateNodeIncoming();
+    //$TODO restore this optimization
+//  if (progress is 0.0 && !(node instanceof VirtualNode))
+//      node.sortValue = -1;
+    double value = evaluateNodeOutgoing();
+    if (value < 0)
+        value = node.index * nextRankSize / rankSize;
+    node.sortValue += value * progress;
+//  if (progress < 0.7 && node.sortValue !is -1)
+//      node.sortValue += Math.random() * rankSize / (5 + 8 * progress);
+}
+
+void sortValueOutgoing() {
+    node.sortValue = evaluateNodeOutgoing();
+    //$TODO restore this optimization
+//  if (progress is 0.0 && !(node instanceof VirtualNode))
+//      node.sortValue = -1;
+    double value = evaluateNodeIncoming();
+    if (value < 0)
+        value = node.index * nextRankSize / rankSize;
+    node.sortValue += value * progress;
+//  if (progress < 0.7 && node.sortValue !is -1)
+//      node.sortValue += Math.random() * rankSize / (5 + 8 * progress);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/RevertableChange.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.graph.RevertableChange;
+
+import dwt.dwthelper.utils;
+
+/**
+ * @since 3.1
+ */
+class RevertableChange {
+
+void revert() { }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/RouteEdges.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.RouteEdges;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.graph.GraphVisitor;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.Edge;
+import dwtx.draw2d.graph.SubgraphBoundary;
+import dwtx.draw2d.graph.ShortestPathRouter;
+import dwtx.draw2d.graph.Path;
+import dwtx.draw2d.graph.VirtualNode;
+import dwtx.draw2d.graph.Node;
+
+/**
+ * @author Randy Hudson
+ */
+class RouteEdges : GraphVisitor {
+
+/**
+ * @see GraphVisitor#visit(DirectedGraph)
+ */
+public void revisit(DirectedGraph g) {
+    for (int i = 0; i < g.edges.size(); i++) {
+        Edge edge = cast(Edge)g.edges.get(i);
+        edge.start = new Point(
+            edge.getSourceOffset() + edge.source.x,
+            edge.source.y + edge.source.height);
+        if (auto boundary = cast(SubgraphBoundary)edge.source ) {
+            if (boundary.getParent().head is boundary)
+                edge.start.y = boundary.getParent().y + boundary.getParent().insets.top;
+        }
+        edge.end = new Point(
+            edge.getTargetOffset() + edge.target.x,
+            edge.target.y);
+
+        if (edge.vNodes !is null)
+            routeLongEdge(edge, g);
+        else {
+            PointList list = new PointList();
+            list.addPoint(edge.start);
+            list.addPoint(edge.end);
+            edge.setPoints(list);
+        }
+    }
+}
+
+static void routeLongEdge(Edge edge, DirectedGraph g) {
+    ShortestPathRouter router = new ShortestPathRouter();
+    Path path = new Path(edge.start, edge.end);
+    router.addPath(path);
+    Rectangle o;
+    Insets padding;
+    for (int i = 0; i < edge.vNodes.size(); i++) {
+        VirtualNode node = cast(VirtualNode)edge.vNodes.get(i);
+        Node neighbor;
+        if (node.left !is null) {
+            neighbor = node.left;
+            o = new Rectangle(neighbor.x, neighbor.y, neighbor.width, neighbor.height);
+            padding = g.getPadding(neighbor);
+            o.width += padding.right + padding.left;
+            o.width += (edge.getPadding() * 2);
+            o.x -= (padding.left + edge.getPadding());
+            o.union_(o.getLocation().translate(-100000, 2));
+            router.addObstacle(o);
+        }
+        if (node.right !is null) {
+            neighbor = node.right;
+            o = new Rectangle(neighbor.x, neighbor.y, neighbor.width, neighbor.height);
+            padding = g.getPadding(neighbor);
+            o.width += padding.right + padding.left;
+            o.width += (edge.getPadding() * 2);
+            o.x -= (padding.left + edge.getPadding());
+            o.union_(o.getLocation().translate(100000, 2));
+            router.addObstacle(o);
+        }
+    }
+    router.setSpacing(0);
+    router.solve();
+    edge.setPoints(path.getPoints());
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/Segment.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.Segment;
+
+import dwt.dwthelper.utils;
+import tango.text.convert.Format;
+
+import dwtx.draw2d.geometry.Geometry;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.graph.Vertex;
+
+/**
+ * A Segment representation for the ShortestPathRouting. A segment is a line between
+ * two vertices.
+ *
+ * This class is for internal use only
+ * @author Whitney Sorenson
+ * @since 3.0
+ */
+class Segment {
+
+Vertex start, end;
+
+/**
+ * Creates a segment between the given start and end points.
+ * @param start the start vertex
+ * @param end the end vertex
+ */
+this(Vertex start, Vertex end) {
+    this.start = start;
+    this.end = end;
+}
+
+/**
+ * Returns the cosine of the made between this segment and the given segment
+ * @param otherSegment the other segment
+ * @return cosine value (not arc-cos)
+ */
+double cosine(Segment otherSegment) {
+    double cos = (((start.x - end.x) * (otherSegment.end.x - otherSegment.start.x))
+            + ((start.y - end.y) * (otherSegment.end.y - otherSegment.start.y)))
+                / (getLength() * otherSegment.getLength());
+    double sin = (((start.x - end.x) * (otherSegment.end.y - otherSegment.start.y))
+            - ((start.y - end.y) * (otherSegment.end.x - otherSegment.start.x)));
+    if (sin < 0.0)
+        return (1 + cos);
+
+    return -(1 + cos);
+}
+
+/**
+ * Returns the cross product of this segment and the given segment
+ * @param otherSegment the other segment
+ * @return the cross product
+ */
+long crossProduct(Segment otherSegment) {
+    return (((start.x - end.x) * (otherSegment.end.y - end.y))
+            - ((start.y - end.y) * (otherSegment.end.x - end.x)));
+}
+
+private double getLength() {
+    return (end.getDistance(start));
+}
+
+/**
+ * Returns a number that represents the sign of the slope of this segment. It does
+ * not return the actual slope.
+ * @return number representing sign of the slope
+ */
+double getSlope() {
+    if (end.x - start.x >= 0)
+        return (end.y - start.y);
+    else
+        return -(end.y - start.y);
+}
+
+/**
+ * Returns true if the given segment intersects this segment.
+ * @param sx start x
+ * @param sy start y
+ * @param tx end x
+ * @param ty end y
+ * @return true if the segments intersect
+ */
+bool intersects(int sx, int sy, int tx, int ty) {
+    return Geometry.linesIntersect(start.x, start.y, end.x, end.y, sx, sy, tx, ty);
+}
+
+/**
+ * Return true if the segment represented by the points intersects this segment.
+ * @param s start point
+ * @param t end point
+ * @return true if the segments intersect
+ */
+bool intersects(Point s, Point t) {
+    return intersects(s.x, s.y, t.x, t.y);
+}
+
+/**
+ * @see java.lang.Object#toString()
+ */
+public String toString() {
+    return Format( "{}---{}", start, end ); //$NON-NLS-1$
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/ShortestPathRouter.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,909 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.ShortestPathRouter;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.PositionConstants;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.graph.Path;
+import dwtx.draw2d.graph.Vertex;
+import dwtx.draw2d.graph.Segment;
+import dwtx.draw2d.graph.Obstacle;
+
+/**
+ * Bends a collection of {@link Path Paths} around rectangular obstacles. This class
+ * maintains a list of paths and obstacles. Updates can be made to the paths and/or
+ * obstacles, and then an incremental solve can be invoked.
+ * <P>
+ * The algorithm will attempt to find the shortest non-intersecting path between each
+ * path's start and end points. Once all paths have been found, they will be offset based
+ * on how many paths bend around the same corner of each obstacle.
+ * <P>
+ * The worst-case performance of this algorithm is p * s * n^2, where p is the number of
+ * paths, n is the number of obstacles, and s is the average number of segments in each
+ * path's final solution.
+ * <P>
+ * This class is not intended to be subclassed.
+ * @author Whitney Sorenson
+ * @author Randy Hudson
+ * @since 3.0
+ */
+public class ShortestPathRouter {
+
+/**
+ * A stack of Paths.
+ */
+static class PathStack : ArrayList {
+
+    Path pop() {
+        return cast(Path)remove(size() - 1);
+    }
+
+    void push(Path path) {
+        add(path);
+    }
+}
+
+/**
+ * The number of times to grow obstacles and test for intersections. This is a tradeoff
+ * between performance and quality of output.
+ */
+private static const int NUM_GROW_PASSES = 2;
+
+private int spacing = 4;
+private bool growPassChangedObstacles;
+private List orderedPaths;
+private Map pathsToChildPaths;
+
+private PathStack stack;
+private List subPaths;
+
+private List userObstacles;
+private List userPaths;
+private List workingPaths;
+
+/**
+ * Creates a new shortest path routing.
+ */
+public this() {
+    userPaths = new ArrayList();
+    workingPaths = new ArrayList();
+    pathsToChildPaths = new HashMap();
+    userObstacles = new ArrayList();
+}
+
+/**
+ * Adds an obstacle with the given bounds to the obstacles.
+ *
+ * @param rect the bounds of this obstacle
+ * @return <code>true</code> if the added obstacle has dirtied one or more paths
+ */
+public bool addObstacle(Rectangle rect) {
+    return internalAddObstacle(new Obstacle(rect, this));
+}
+
+/**
+ * Adds a path to the routing.
+ * @param path the path to add.
+ */
+public void addPath(Path path) {
+    userPaths.add(path);
+    workingPaths.add(path);
+}
+
+/**
+ * Fills the point lists of the Paths to the correct bent points.
+ */
+private void bendPaths() {
+    for (int i = 0; i < orderedPaths.size(); i++) {
+        Path path = cast(Path) orderedPaths.get(i);
+        Segment segment = null;
+        path.points.addPoint(new Point(path.start.x, path.start.y));
+        for (int v = 0; v < path.grownSegments.size(); v++) {
+            segment = cast(Segment) path.grownSegments.get(v);
+            Vertex vertex = segment.end;
+
+            if (vertex !is null && v < path.grownSegments.size() - 1) {
+                if (vertex.type is Vertex.INNIE) {
+                    vertex.count++;
+                    path.points.addPoint(vertex.bend(vertex.count));
+                } else {
+                    path.points.addPoint(vertex.bend(vertex.totalCount));
+                    vertex.totalCount--;
+                }
+            }
+        }
+        path.points.addPoint(new Point(path.end.x, path.end.y));
+    }
+}
+
+/**
+ * Checks a vertex to see if its offset should shrink
+ * @param vertex the vertex to check
+ */
+private void checkVertexForIntersections(Vertex vertex) {
+    if (vertex.nearestObstacle !is 0 || vertex.nearestObstacleChecked)
+        return;
+    int sideLength, x, y;
+
+    sideLength = 2 * (vertex.totalCount * getSpacing()) + 1;
+
+    if ((vertex.positionOnObstacle & PositionConstants.NORTH) > 0)
+        y = vertex.y - sideLength;
+    else
+        y = vertex.y;
+    if ((vertex.positionOnObstacle & PositionConstants.EAST) > 0)
+        x = vertex.x;
+    else
+        x = vertex.x - sideLength;
+
+    Rectangle r = new Rectangle(x, y, sideLength, sideLength);
+
+    int xDist, yDist;
+
+    for (int o = 0; o < userObstacles.size(); o++) {
+        Obstacle obs = cast(Obstacle)userObstacles.get(o);
+        if (obs !is vertex.obs && r.intersects(obs)) {
+            int pos = obs.getPosition(vertex);
+            if (pos is 0)
+                continue;
+
+            if ((pos & PositionConstants.NORTH) > 0)
+                //   use top
+                yDist = obs.y - vertex.y;
+            else
+                // use bottom
+                yDist = vertex.y - obs.bottom() + 1;
+            if ((pos & PositionConstants.EAST) > 0)
+                //   use right
+                xDist = vertex.x - obs.right() + 1;
+            else
+                //   use left
+                xDist = obs.x - vertex.x;
+
+            if (Math.max(xDist, yDist) < vertex.nearestObstacle
+                    || vertex.nearestObstacle is 0) {
+                vertex.nearestObstacle = Math.max(xDist, yDist);
+                vertex.updateOffset();
+            }
+
+        }
+    }
+
+    vertex.nearestObstacleChecked = true;
+}
+
+/**
+ * Checks all vertices along paths for intersections
+ */
+private void checkVertexIntersections() {
+    for (int i = 0; i < workingPaths.size(); i++) {
+        Path path = cast(Path)workingPaths.get(i);
+
+        for (int s = 0; s < path.segments.size() - 1; s++) {
+            Vertex vertex = (cast(Segment)path.segments.get(s)).end;
+            checkVertexForIntersections(vertex);
+        }
+    }
+}
+
+/**
+ * Frees up fields which aren't needed between invocations.
+ */
+private void cleanup() {
+    for (int i = 0; i < workingPaths.size(); i++) {
+        Path path = cast(Path)workingPaths.get(i);
+        path.cleanup();
+    }
+}
+
+/**
+ * Counts how many paths are on given vertices in order to increment their total count.
+ */
+private void countVertices() {
+    for (int i = 0; i < workingPaths.size(); i++) {
+        Path path = cast(Path) workingPaths.get(i);
+        for (int v = 0; v < path.segments.size() - 1; v++)
+            (cast(Segment)path.segments.get(v)).end.totalCount++;
+    }
+}
+
+/**
+ * Dirties the paths that are on the given vertex
+ * @param vertex the vertex that has the paths
+ */
+private bool dirtyPathsOn(Vertex vertex) {
+    List paths = vertex.paths;
+    if (paths !is null && paths.size() !is 0) {
+        for (int i = 0; i < paths.size(); i++)
+            (cast(Path)paths.get(i)).isDirty = true;
+        return true;
+    }
+    return false;
+}
+
+/**
+ * Resyncs the parent paths with any new child paths that are necessary because bendpoints
+ * have been added to the parent path.
+ *
+private void generateChildPaths() {
+    for (int i = 0; i < userPaths.size(); i++) {
+        Path path = (Path)userPaths.get(i);
+        PointList bendPoints = path.bendpoints;
+        if (bendPoints !is null && bendPoints.size() !is 0) {
+            List childPaths = new ArrayList(bendPoints.size() + 1);
+            Path child = null;
+            Vertex prevVertex = path.start;
+            Vertex currVertex = null;
+
+            for (int b = 0; b < bendPoints.size(); b++) {
+                Point bp = (Point)bendPoints.getPoint(b);
+                currVertex = new Vertex(bp, null);
+                child = new Path(prevVertex, currVertex);
+                childPaths.add(child);
+                workingPaths.add(child);
+                prevVertex = currVertex;
+            }
+
+            child = new Path(prevVertex, path.end);
+            childPaths.add(child);
+            workingPaths.add(child);
+            pathsToChildPaths.put(path, childPaths);
+        } else
+            workingPaths.add(path);
+    } //End FOR
+}*/
+
+/**
+ * Returns the closest vertex to the given segment.
+ * @param v1 the first vertex
+ * @param v2 the second vertex
+ * @param segment the segment
+ * @return v1, or v2 whichever is closest to the segment
+ */
+private Vertex getNearestVertex(Vertex v1, Vertex v2, Segment segment) {
+    if (segment.start.getDistance(v1) + segment.end.getDistance(v1)
+            > segment.start.getDistance(v2) + segment.end.getDistance(v2))
+        return v2;
+    else
+        return v1;
+}
+
+/**
+ * Returns the spacing maintained between paths.
+ * @return the default path spacing
+ * @see #setSpacing(int)
+ * @since 3.2
+ */
+public int getSpacing() {
+    return spacing;
+}
+
+/**
+ * Returns the subpath for a split on the given path at the given segment.
+ * @param path the path
+ * @param segment the segment
+ * @return the new subpath
+ */
+private Path getSubpathForSplit(Path path, Segment segment) {
+    Path newPath = path.getSubPath(segment);
+    workingPaths.add(newPath);
+    subPaths.add(newPath);
+    return newPath;
+}
+
+/**
+ * Grows all obstacles in in routing and tests for new intersections
+ */
+private void growObstacles() {
+    growPassChangedObstacles = false;
+    for (int i = 0; i < NUM_GROW_PASSES; i++) {
+        if (i is 0 || growPassChangedObstacles)
+            growObstaclesPass();
+    }
+}
+
+/**
+ * Performs a single pass of the grow obstacles step, this can be repeated as desired.
+ * Grows obstacles, then tests paths against the grown obstacles.
+ */
+private void growObstaclesPass() {
+    // grow obstacles
+    for (int i = 0; i < userObstacles.size(); i++)
+        (cast(Obstacle)userObstacles.get(i)).growVertices();
+
+    // go through paths and test segments
+    for (int i = 0; i < workingPaths.size(); i++) {
+        Path path = cast(Path) workingPaths.get(i);
+
+        for (int e = 0; e < path.excludedObstacles.size(); e++)
+            (cast(Obstacle)path.excludedObstacles.get(e)).exclude = true;
+
+        if (path.grownSegments.size() is 0) {
+            for (int s = 0; s < path.segments.size(); s++)
+                testOffsetSegmentForIntersections(cast(Segment)path.segments.get(s), -1, path);
+        } else {
+            int counter = 0;
+            List currentSegments = new ArrayList(path.grownSegments);
+            for (int s = 0; s < currentSegments.size(); s++)
+                counter += testOffsetSegmentForIntersections(cast(Segment)currentSegments.get(s), s + counter, path);
+        }
+
+        for (int e = 0; e < path.excludedObstacles.size(); e++)
+            (cast(Obstacle)path.excludedObstacles.get(e)).exclude = false;
+
+    }
+
+    // revert obstacles
+    for (int i = 0; i < userObstacles.size(); i++)
+        (cast(Obstacle)userObstacles.get(i)).shrinkVertices();
+}
+
+/**
+ * Adds an obstacle to the routing
+ * @param obs the obstacle
+ */
+private bool internalAddObstacle(Obstacle obs) {
+    userObstacles.add(obs);
+    return testAndDirtyPaths(obs);
+}
+
+/**
+ * Removes an obstacle from the routing.
+ * @param rect the bounds of the obstacle
+ * @return the obstacle removed
+ */
+private bool internalRemoveObstacle(Rectangle rect) {
+    Obstacle obs = null;
+    int index = -1;
+    for (int i = 0; i < userObstacles.size(); i++) {
+        obs = cast(Obstacle)userObstacles.get(i);
+        if (obs.opEquals(rect)) {
+            index = i;
+            break;
+        }
+    }
+
+    userObstacles.remove(index);
+
+    bool result = false;
+    result |= dirtyPathsOn(obs.bottomLeft);
+    result |= dirtyPathsOn(obs.topLeft);
+    result |= dirtyPathsOn(obs.bottomRight);
+    result |= dirtyPathsOn(obs.topRight);
+
+    for (int p = 0; p < workingPaths.size(); p++) {
+        Path path = cast(Path)workingPaths.get(p);
+        if (path.isDirty)
+            continue;
+        if (path.isObstacleVisible(obs))
+            path.isDirty = result = true;
+    }
+
+    return result;
+}
+
+/**
+ * Labels the given path's vertices as innies, or outies, as well as determining if this
+ * path is inverted.
+ * @param path the path
+ */
+private void labelPath(Path path) {
+    Segment segment = null;
+    Segment nextSegment = null;
+    Vertex vertex = null;
+    bool agree = false;
+    for (int v = 0; v < path.grownSegments.size() - 1; v++) {
+        segment = cast(Segment) path.grownSegments.get(v);
+        nextSegment = cast(Segment) path.grownSegments.get(v + 1);
+        vertex = segment.end;
+        long crossProduct = segment.crossProduct(new Segment(vertex, vertex.obs.center));
+
+        if (vertex.type is Vertex.NOT_SET) {
+            labelVertex(segment, crossProduct, path);
+        } else if (!path.isInverted
+                && ((crossProduct > 0 && vertex.type is Vertex.OUTIE)
+                        || (crossProduct < 0 && vertex.type is Vertex.INNIE))) {
+            if (agree) {
+                // split detected.
+                stack.push(getSubpathForSplit(path, segment));
+                return;
+            } else {
+                path.isInverted = true;
+                path.invertPriorVertices(segment);
+            }
+        } else if (path.isInverted
+                && ((crossProduct < 0 && vertex.type is Vertex.OUTIE)
+                        || (crossProduct > 0 && vertex.type is Vertex.INNIE))) {
+            // split detected.
+            stack.push(getSubpathForSplit(path, segment));
+            return;
+        } else
+            agree = true;
+
+        if (vertex.paths !is null) {
+            for (int i = 0;i < vertex.paths.size();i++) {
+                Path nextPath = cast(Path)vertex.paths.get(i);
+                if (!nextPath.isMarked) {
+                    nextPath.isMarked = true;
+                    stack.push(nextPath);
+                }
+            }
+        }
+
+        vertex.addPath(path, segment, nextSegment);
+    }
+}
+
+/**
+ * Labels all path's vertices in the routing.
+ */
+private void labelPaths() {
+    Path path = null;
+    for (int i = 0; i < workingPaths.size(); i++) {
+        path = cast(Path) workingPaths.get(i);
+        stack.push(path);
+    }
+
+    while (!stack.isEmpty()) {
+        path = stack.pop();
+        if (!path.isMarked) {
+            path.isMarked = true;
+            labelPath(path);
+        }
+    }
+
+    // revert is marked so we can use it again in ordering.
+    for (int i = 0;i < workingPaths.size(); i++) {
+        path = cast(Path)workingPaths.get(i);
+        path.isMarked = false;
+    }
+}
+
+/**
+ * Labels the vertex at the end of the semgent based on the cross product.
+ * @param segment the segment to this vertex
+ * @param crossProduct the cross product of this segment and a segment to the obstacles center
+ * @param path the path
+ */
+private void labelVertex(Segment segment, long crossProduct, Path path) {
+//   assumes vertex in question is segment.end
+    if (crossProduct > 0) {
+        if (path.isInverted)
+            segment.end.type = Vertex.OUTIE;
+        else
+            segment.end.type = Vertex.INNIE;
+    } else if (crossProduct < 0) {
+        if (path.isInverted)
+            segment.end.type = Vertex.INNIE;
+        else
+            segment.end.type = Vertex.OUTIE;
+    } else if (segment.start.type !is Vertex.NOT_SET)
+        segment.end.type = segment.start.type;
+    else
+        segment.end.type = Vertex.INNIE;
+}
+
+/**
+ * Orders the path by comparing its angle at shared vertices with other paths.
+ * @param path the path
+ */
+private void orderPath(Path path) {
+    if (path.isMarked)
+        return;
+    path.isMarked = true;
+    Segment segment = null;
+    Vertex vertex = null;
+    for (int v = 0; v < path.grownSegments.size() - 1; v++) {
+        segment = cast(Segment) path.grownSegments.get(v);
+        vertex = segment.end;
+        double thisAngle = (cast(Double)vertex.cachedCosines.get(path)).doubleValue();
+        if (path.isInverted)
+            thisAngle = -thisAngle;
+
+        for (int i = 0; i < vertex.paths.size(); i++) {
+            Path vPath = cast(Path)vertex.paths.get(i);
+            if (!vPath.isMarked) {
+                double otherAngle = (cast(Double)vertex.cachedCosines.get(vPath)).doubleValue();
+
+                if (vPath.isInverted)
+                    otherAngle = -otherAngle;
+
+                if (otherAngle < thisAngle)
+                    orderPath(vPath);
+            }
+        }
+    }
+
+    orderedPaths.add(path);
+}
+
+/**
+ * Orders all paths in the graph.
+ */
+private void orderPaths() {
+    for (int i = 0; i < workingPaths.size(); i++) {
+        Path path = cast(Path) workingPaths.get(i);
+        orderPath(path);
+    }
+}
+
+/**
+ * Populates the parent paths with all the child paths that were created to represent
+ * bendpoints.
+ */
+private void recombineChildrenPaths() {
+    // only populate those paths with children paths.
+    Iterator keyItr = pathsToChildPaths.keySet().iterator();
+    while (keyItr.hasNext()) {
+        Path path = cast(Path)keyItr.next();
+
+        path.fullReset();
+
+        List childPaths = cast(List)pathsToChildPaths.get(path);
+        Path childPath = null;
+
+        for (int i = 0; i < childPaths.size(); i++) {
+            childPath = cast(Path)childPaths.get(i);
+            path.points.addAll(childPath.getPoints());
+            // path will overlap
+            path.points.removePoint(path.points.size() - 1);
+            //path.grownSegments.addAll(childPath.grownSegments);
+            path.segments.addAll(childPath.segments);
+            path.visibleObstacles.addAll(childPath.visibleObstacles);
+        }
+
+        // add last point.
+        path.points.addPoint(childPath.points.getLastPoint());
+    }
+}
+
+/**
+ * Reconnects all subpaths.
+ */
+private void recombineSubpaths() {
+    for (int p = 0; p < orderedPaths.size(); p++) {
+        Path path = cast(Path)orderedPaths.get(p);
+        path.reconnectSubPaths();
+    }
+
+    orderedPaths.removeAll(subPaths);
+    workingPaths.removeAll(subPaths);
+    subPaths = null;
+}
+
+/**
+ * Removes the obstacle with the rectangle's bounds from the routing.
+ *
+ * @param rect the bounds of the obstacle to remove
+ * @return <code>true</code> if the removal has dirtied one or more paths
+ */
+public bool removeObstacle(Rectangle rect) {
+    return internalRemoveObstacle(rect);
+}
+
+/**
+ * Removes the given path from the routing.
+ *
+ * @param path the path to remove.
+ * @return <code>true</code> if the removal may have affected one of the remaining paths
+ */
+public bool removePath(Path path) {
+    userPaths.remove(path);
+    List children = cast(List)pathsToChildPaths.get(path);
+    if (children is null)
+        workingPaths.remove(path);
+    else
+        workingPaths.removeAll(children);
+    return true;
+}
+
+/**
+ * Resets exclude field on all obstacles
+ */
+private void resetObstacleExclusions() {
+    for (int i = 0; i < userObstacles.size(); i++)
+        (cast(Obstacle)userObstacles.get(i)).exclude = false;
+}
+
+/**
+ * Resets all vertices found on paths and obstacles.
+ */
+private void resetVertices() {
+    for (int i = 0; i < userObstacles.size(); i++) {
+        Obstacle obs = cast(Obstacle)userObstacles.get(i);
+        obs.reset();
+    }
+    for (int i = 0; i < workingPaths.size(); i++) {
+        Path path = cast(Path)workingPaths.get(i);
+        path.start.fullReset();
+        path.end.fullReset();
+    }
+}
+
+/**
+ * Sets the default spacing between paths. The spacing is the minimum distance that path
+ * should be offset from other paths or obstacles. The default value is 4. When this value
+ * can not be satisfied, paths will be squeezed together uniformly.
+ * @param spacing the path spacing
+ * @since 3.2
+ */
+public void setSpacing(int spacing) {
+    this.spacing = spacing;
+}
+
+/**
+ * Updates the points in the paths in order to represent the current solution
+ * with the given paths and obstacles.
+ *
+ * @return returns the list of paths which were updated.
+ */
+public List solve() {
+
+    solveDirtyPaths();
+
+    countVertices();
+    checkVertexIntersections();
+    growObstacles();
+
+    subPaths = new ArrayList();
+    stack = new PathStack();
+    labelPaths();
+    stack = null;
+
+    orderedPaths = new ArrayList();
+    orderPaths();
+    bendPaths();
+
+    recombineSubpaths();
+    orderedPaths = null;
+    subPaths = null;
+
+    recombineChildrenPaths();
+    cleanup();
+
+    return Collections.unmodifiableList(userPaths);
+}
+
+/**
+ * Solves paths that are dirty.
+ * @return number of dirty paths
+ */
+private int solveDirtyPaths() {
+    int numSolved = 0;
+
+    for (int i = 0; i < userPaths.size(); i++) {
+        Path path = cast(Path)userPaths.get(i);
+        if (!path.isDirty)
+            continue;
+        List children = cast(List)pathsToChildPaths.get(path);
+        int prevCount = 1, newCount = 1;
+        if (children is null)
+            children = Collections.EMPTY_LIST;
+        else
+            prevCount = children.size();
+
+        if (path.getBendPoints() !is null)
+            newCount = path.getBendPoints().size() + 1;
+
+        if (prevCount !is newCount)
+            children = regenerateChildPaths(path, children, prevCount, newCount);
+        refreshChildrenEndpoints(path, children);
+    }
+
+    for (int i = 0; i < workingPaths.size(); i++) {
+        Path path = cast(Path)workingPaths.get(i);
+        path.refreshExcludedObstacles(userObstacles);
+        if (!path.isDirty) {
+            path.resetPartial();
+            continue;
+        }
+
+        numSolved++;
+        path.fullReset();
+
+        bool pathFoundCheck = path.generateShortestPath(userObstacles);
+        if (!pathFoundCheck || path.end.cost > path.threshold) {
+            // path not found, or path found was too long
+            resetVertices();
+            path.fullReset();
+            path.threshold = 0;
+            pathFoundCheck = path.generateShortestPath(userObstacles);
+        }
+
+        resetVertices();
+    }
+
+    resetObstacleExclusions();
+
+    if (numSolved is 0)
+        resetVertices();
+
+    return numSolved;
+}
+
+/**
+ * @since 3.0
+ * @param path
+ * @param children
+ */
+private void refreshChildrenEndpoints(Path path, List children) {
+    Point previous = path.getStartPoint();
+    Point next;
+    PointList bendpoints = path.getBendPoints();
+    Path child;
+
+    for (int i = 0; i < children.size(); i++) {
+        if (i < bendpoints.size())
+            next = bendpoints.getPoint(i);
+        else
+            next = path.getEndPoint();
+        child = cast(Path)children.get(i);
+        child.setStartPoint(previous);
+        child.setEndPoint(next);
+        previous = next;
+    }
+}
+
+/**
+ * @since 3.0
+ * @param path
+ * @param children
+ */
+private List regenerateChildPaths(Path path, List children, int currentSize, int newSize) {
+    //Path used to be simple but now is compound, children is EMPTY.
+    if (currentSize is 1) {
+        workingPaths.remove(path);
+        currentSize = 0;
+        children = new ArrayList(newSize);
+        pathsToChildPaths.put(path, cast(Object)children);
+    } else
+    //Path is becoming simple but was compound.  children becomes empty.
+    if (newSize is 1) {
+        workingPaths.removeAll(children);
+        workingPaths.add(path);
+        pathsToChildPaths.remove(path);
+        return Collections.EMPTY_LIST;
+    }
+
+    //Add new working paths until the sizes are the same
+    while (currentSize < newSize) {
+        Path child = new Path();
+        workingPaths.add(child);
+        children.add(child);
+        currentSize++;
+    }
+
+    while (currentSize > newSize) {
+        Path child = cast(Path)children.remove(children.size() - 1);
+        workingPaths.remove(child);
+        currentSize--;
+    }
+
+    return children;
+}
+
+/**
+ * Tests a segment that has been offset for new intersections
+ * @param segment the segment
+ * @param index the index of the segment along the path
+ * @param path the path
+ * @return 1 if new segments have been inserted
+ */
+private int testOffsetSegmentForIntersections(Segment segment, int index, Path path) {
+    for (int i = 0; i < userObstacles.size(); i++) {
+        Obstacle obs = cast(Obstacle) userObstacles.get(i);
+
+        if (segment.end.obs is obs || segment.start.obs is obs || obs.exclude)
+            continue;
+        Vertex vertex = null;
+
+        int offset = getSpacing();
+        if (segment.getSlope() < 0) {
+            if (segment.intersects(obs.topLeft.x - offset,
+                    obs.topLeft.y - offset,
+                    obs.bottomRight.x + offset,
+                    obs.bottomRight.y + offset))
+                vertex = getNearestVertex(obs.topLeft, obs.bottomRight, segment);
+            else if (segment.intersects(obs.bottomLeft.x - offset,
+                    obs.bottomLeft.y + offset,
+                    obs.topRight.x + offset,
+                    obs.topRight.y - offset))
+                vertex = getNearestVertex(obs.bottomLeft, obs.topRight, segment);
+        } else {
+            if (segment.intersects(obs.bottomLeft.x - offset,
+                    obs.bottomLeft.y + offset,
+                    obs.topRight.x + offset,
+                    obs.topRight.y - offset))
+                vertex = getNearestVertex(obs.bottomLeft, obs.topRight, segment);
+            else if (segment.intersects(obs.topLeft.x - offset,
+                    obs.topLeft.y - offset,
+                    obs.bottomRight.x + offset,
+                    obs.bottomRight.y + offset))
+                vertex = getNearestVertex(obs.topLeft, obs.bottomRight, segment);
+        }
+
+        if (vertex !is null) {
+            Rectangle vRect = vertex.getDeformedRectangle(offset);
+            if (segment.end.obs !is null) {
+                Rectangle endRect = segment.end.getDeformedRectangle(offset);
+                if (vRect.intersects(endRect))
+                    continue;
+            }
+            if (segment.start.obs !is null) {
+                Rectangle startRect = segment.start.getDeformedRectangle(offset);
+                if (vRect.intersects(startRect))
+                    continue;
+            }
+
+            Segment newSegmentStart = new Segment(segment.start, vertex);
+            Segment newSegmentEnd = new Segment(vertex, segment.end);
+
+            vertex.totalCount++;
+            vertex.nearestObstacleChecked = false;
+
+            vertex.shrink();
+            checkVertexForIntersections(vertex);
+            vertex.grow();
+
+            if (vertex.nearestObstacle !is 0)
+                vertex.updateOffset();
+
+            growPassChangedObstacles = true;
+
+            if (index !is -1) {
+                path.grownSegments.remove(segment);
+                path.grownSegments.add(index, newSegmentStart);
+                path.grownSegments.add(index + 1, newSegmentEnd);
+            } else {
+                path.grownSegments.add(newSegmentStart);
+                path.grownSegments.add(newSegmentEnd);
+            }
+            return 1;
+        }
+    }
+    if (index is -1)
+        path.grownSegments.add(segment);
+    return 0;
+}
+
+/**
+ * Tests all paths against the given obstacle
+ * @param obs the obstacle
+ */
+private bool testAndDirtyPaths(Obstacle obs) {
+    bool result = false;
+    for (int i = 0; i < workingPaths.size(); i++) {
+        Path path = cast(Path)workingPaths.get(i);
+        result |= path.testAndSet(obs);
+    }
+    return result;
+}
+
+/**
+ * Updates the position of an existing obstacle.
+ * @param oldBounds the old bounds(used to find the obstacle)
+ * @param newBounds the new bounds
+ * @return <code>true</code> if the change the current results to become stale
+ */
+public bool updateObstacle(Rectangle oldBounds, Rectangle newBounds) {
+    bool result = internalRemoveObstacle(oldBounds);
+    result |= addObstacle(newBounds);
+    return result;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/SortSubgraphs.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,238 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.SortSubgraphs;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.draw2d.graph.GraphVisitor;
+import dwtx.draw2d.graph.NestingTree;
+import dwtx.draw2d.graph.CompoundDirectedGraph;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.NodeList;
+import dwtx.draw2d.graph.NodePair;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.RankList;
+import dwtx.draw2d.graph.Rank;
+import dwtx.draw2d.graph.Subgraph;
+
+/**
+ * Performs a topological sort from left to right of the subgraphs in a compound directed
+ * graph.  This ensures that subgraphs do not intertwine.
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+class SortSubgraphs : GraphVisitor {
+
+CompoundDirectedGraph g;
+
+NestingTree[] nestingTrees;
+
+Set orderingGraphEdges;
+Set orderingGraphNodes;
+NodePair pair;
+
+public this(){
+    orderingGraphEdges = new HashSet();
+    orderingGraphNodes = new HashSet();
+    pair = new NodePair();
+}
+
+private void breakSubgraphCycles() {
+    //The stack of nodes which have no unmarked incoming edges
+    List noLefts = new ArrayList();
+
+    int index = 1;
+    //Identify all initial nodes for removal
+    for (Iterator iter = orderingGraphNodes.iterator(); iter.hasNext();) {
+        Node node = cast(Node)iter.next();
+        if (node.x is 0)
+            sortedInsert(noLefts, node);
+    }
+
+    Node cycleRoot;
+    do {
+        //Remove all leftmost nodes, updating the nodes to their right
+        while (noLefts.size() > 0) {
+            Node node = cast(Node)noLefts.remove(noLefts.size() - 1);
+            node.sortValue = index++;
+            orderingGraphNodes.remove(node);
+//          System.out.println("removed:" + node);
+            NodeList rightOf = rightOf(node);
+            if (rightOf is null)
+                continue;
+            for (int i = 0; i < rightOf.size(); i++) {
+                Node right = rightOf.getNode(i);
+                right.x--;
+                if (right.x is 0)
+                    sortedInsert(noLefts, right);
+            }
+        }
+        cycleRoot = null;
+        double min = Double.MAX_VALUE;
+        for (Iterator iter = orderingGraphNodes.iterator(); iter.hasNext();) {
+            Node node = cast(Node)iter.next();
+            if (node.sortValue < min) {
+                cycleRoot = node;
+                min = node.sortValue;
+            }
+        }
+        if (cycleRoot !is null) {
+            //break the cycle;
+            sortedInsert(noLefts, cycleRoot);
+//          System.out.println("breaking cycle with:" + cycleRoot);
+//          Display.getCurrent().beep();
+            cycleRoot.x = -1; //prevent x from ever reaching 0
+        } // else if (OGmembers.size() > 0)
+            //System.out.println("FAILED TO FIND CYCLE ROOT"); //$NON-NLS-1$
+    } while (cycleRoot !is null);
+}
+
+private void buildSubgraphOrderingGraph() {
+    RankList ranks = g.ranks;
+    nestingTrees = new NestingTree[ranks.size()];
+    for (int r = 0; r < ranks.size(); r++) {
+        NestingTree entry = NestingTree.buildNestingTreeForRank(ranks.getRank(r));
+        nestingTrees[r] = entry;
+        entry.calculateSortValues();
+        entry.recursiveSort(false);
+    }
+
+    for (int i = 0; i < nestingTrees.length; i++) {
+        NestingTree entry = nestingTrees[i];
+        buildSubgraphOrderingGraph(entry);
+    }
+}
+
+private void buildSubgraphOrderingGraph(NestingTree entry) {
+    NodePair pair = new NodePair();
+    if (entry.isLeaf)
+        return;
+    for (int i = 0; i < entry.contents.size(); i++) {
+        Object right = entry.contents.get(i);
+        if (auto r = cast(Node)right )
+            pair.n2 = r;
+        else {
+            pair.n2 = (cast(NestingTree)right).subgraph;
+            buildSubgraphOrderingGraph(cast(NestingTree)right);
+        }
+        if (pair.n1 !is null && !orderingGraphEdges.contains(pair)) {
+            orderingGraphEdges.add(pair);
+            leftToRight(pair.n1, pair.n2);
+            orderingGraphNodes.add(pair.n1);
+            orderingGraphNodes.add(pair.n2);
+            pair.n2.x++; //Using x field to count predecessors.
+            pair = new NodePair(pair.n2, null);
+        } else {
+            pair.n1 = pair.n2;
+        }
+    }
+}
+
+/**
+ * Calculates the average position P for each node and subgraph.  The average position is
+ * stored in the sortValue for each node or subgraph.
+ *
+ * Runs in approximately linear time with respect to the number of nodes, including
+ * virtual nodes.
+ */
+private void calculateSortValues() {
+    RankList ranks = g.ranks;
+
+    g.subgraphs.resetSortValues();
+    g.subgraphs.resetIndices();
+
+    /*
+     * For subgraphs, the sum of all positions is kept, along with the number of
+     * contributions, which is tracked in the subgraph's index field.
+     */
+    for (int r = 0; r < ranks.size(); r++) {
+        Rank rank = ranks.getRank(r);
+        for (int j = 0; j < rank.count(); j++) {
+            Node node = rank.getNode(j);
+            node.sortValue = node.index;
+            Subgraph parent = node.getParent();
+            while (parent !is null) {
+                parent.sortValue += node.sortValue;
+                parent.index++;
+                parent = parent.getParent();
+            }
+        }
+    }
+
+    /*
+     * For each subgraph, divide the sum of the positions by the number of contributions,
+     * to give the average position.
+     */
+    for (int i = 0; i < g.subgraphs.size(); i++) {
+        Subgraph subgraph = cast(Subgraph)g.subgraphs.get(i);
+        subgraph.sortValue /= subgraph.index;
+    }
+}
+
+private void repopulateRanks() {
+    for (int i = 0; i < nestingTrees.length; i++) {
+        Rank rank = g.ranks.getRank(i);
+        rank.clear();
+        nestingTrees[i].repopulateRank(rank);
+    }
+}
+
+private NodeList rightOf(Node left) {
+    return cast(NodeList)left.workingData[0];
+}
+
+private void leftToRight(Node left, Node right) {
+    rightOf(left).add(right);
+}
+
+void sortedInsert(List list, Node node) {
+    int insert = 0;
+    while (insert < list.size()
+      && (cast(Node)list.get(insert)).sortValue > node.sortValue)
+        insert++;
+    list.add(insert, node);
+}
+
+private void topologicalSort() {
+    for (int i = 0; i < nestingTrees.length; i++) {
+        nestingTrees[i].getSortValueFromSubgraph();
+        nestingTrees[i].recursiveSort(false);
+    }
+}
+
+void init() {
+    for (int r = 0; r < g.ranks.size(); r++) {
+        Rank rank = g.ranks.getRank(r);
+        for (int i = 0; i < rank.count(); i++) {
+            Node n = cast(Node)rank.get(i);
+            n.workingData[0] = new NodeList();
+        }
+    }
+    for (int i = 0; i < g.subgraphs.size(); i++) {
+        Subgraph s = cast(Subgraph)g.subgraphs.get(i);
+        s.workingData[0] = new NodeList();
+    }
+}
+
+public void visit(DirectedGraph dg) {
+    g = cast(CompoundDirectedGraph)dg;
+
+    init();
+    buildSubgraphOrderingGraph();
+    calculateSortValues();
+    breakSubgraphCycles();
+    topologicalSort();
+    repopulateRanks();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/SpanningTreeVisitor.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.SpanningTreeVisitor;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.graph.GraphVisitor;
+import dwtx.draw2d.graph.Edge;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.EdgeList;
+
+/**
+ * A base class for visitors which operate on the graphs spanning tree used to induce rank
+ * assignments.
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+abstract class SpanningTreeVisitor : GraphVisitor {
+
+Edge getParentEdge(Node node) {
+    return cast(Edge)node.workingData[1];
+}
+
+EdgeList getSpanningTreeChildren(Node node) {
+    return cast(EdgeList)node.workingData[0];
+}
+
+protected Node getTreeHead(Edge edge) {
+    if (getParentEdge(edge.source) is edge)
+        return edge.target;
+    return edge.source;
+}
+
+Node getTreeParent(Node node) {
+    Edge e = getParentEdge(node);
+    if (e is null)
+        return null;
+    return e.opposite(node);
+}
+
+protected Node getTreeTail(Edge edge) {
+    if (getParentEdge(edge.source) is edge)
+        return edge.source;
+    return edge.target;
+}
+
+void setParentEdge(Node node, Edge edge) {
+    node.workingData[1] = edge;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/Subgraph.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.Subgraph;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.NodeList;
+
+
+/**
+ * A Node which may contain other nodes.  A Subgraph is a compound or container node.  It
+ * may have incoming and outgoing edges just like a node.  Subgraphs are used in {@link
+ * CompoundDirectedGraph}s.  A proper layout of a compound graph ensures that all of a
+ * subgraph's children are placed inside its rectangular region.  Nodes which do not
+ * belong to the subgraph must be placed outside that region.
+ * <P>
+ * A Subgraph may contain another Subgraph.
+ * <P>
+ * A Subgraph has additional geometric properties which describe the containing box.  They
+ * are:
+ * <UL>
+ *   <LI>{@link #insets} - the size of the subgraph's border.  A subgraph is typically
+ *   rendered as a thin rectangular box.  Sometimes this box is labeled or decorated.  The
+ *   insets can be used to reserve space for this purpose.
+ *   <LI>{@link #innerPadding} - the amount of empty space that must be preserved just
+ *   inside the subgraph's border.  This is the minimum space between the border, and the
+ *   children node's contained inside the subgraph.
+ * </UL>
+ *
+ * @author hudsonr
+ * @since 2.1.2
+ */
+public class Subgraph : Node {
+
+/**
+ * The children of this subgraph. Nodes may not belong to more than one subgraph.
+ */
+public NodeList members;
+
+Node head;
+Node tail;
+Node left;
+Node right;
+int nestingTreeMin;
+
+/**
+ * The space required for this subgraph's border.  The default value is undefined.
+ */
+public Insets insets;
+
+/**
+ * The minimum space between this subgraph's border and it's children.
+ */
+public Insets innerPadding;
+
+private static const Insets NO_INSETS;
+static this(){
+    NO_INSETS = new Insets();
+}
+/**
+ * Constructs a new subgraph with the given data object.
+ * @see Node#Node(Object)
+ * @param data an arbitrary data object
+ */
+public this(Object data) {
+    this(data, null);
+}
+
+/**
+ * Constructs a new subgraph with the given data object and parent subgraph.
+ * @see Node#Node(Object, Subgraph)
+ * @param data an arbitrary data object
+ * @param parent the parent
+ */
+public this(Object data, Subgraph parent) {
+    super(data, parent);
+    innerPadding = NO_INSETS;
+    members = new NodeList();
+    insets = new Insets(1);
+}
+
+/**
+ * Adds the given node to this subgraph.
+ * @param n the node to add
+ */
+public void addMember(Node n) {
+    members.add(n);
+}
+
+/**
+ * Returns <code>true</code> if the given node is contained inside the branch represented
+ * by this subgraph.
+ * @param n the node in question
+ * @return <code>true</code> if nested
+ */
+bool isNested(Node n) {
+    return n.nestingIndex >= nestingTreeMin
+        && n.nestingIndex <= nestingIndex;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/SubgraphBoundary.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.SubgraphBoundary;
+
+import dwt.dwthelper.utils;
+import tango.text.convert.Format;
+
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.Subgraph;
+
+/**
+ * For INTERNAL use only.
+ * @author hudsonr
+ * @since 2.1.2
+ */
+class SubgraphBoundary : Node {
+
+/**
+ * constant indicating TOP.
+ */
+public static const int TOP = 0;
+
+/**
+ * constant indicating LEFT.
+ */
+public static const int LEFT = 1;
+
+/**
+ * constant indicating BOTTOM.
+ */
+public static const int BOTTOM = 2;
+
+/**
+ * constant indicating RIGHT.
+ */
+public static const int RIGHT = 3;
+
+/**
+ * Constructs a new boundary.
+ * @param s the subgraph
+ * @param p the padding
+ * @param side which side
+ */
+public this(Subgraph s, Insets p, int side) {
+    super(null, s);
+    this.width = s.width;
+    this.height = s.height;
+    this.padding = new Insets();
+    switch (side) {
+        case LEFT :
+            width = s.insets.left;
+            y = s.y;
+            padding.left = p.left;
+            padding.right = s.innerPadding.left;
+            padding.top = padding.bottom = 0;
+            setParent(s.getParent());
+            data = stringcast(Format("left({})", s )); //$NON-NLS-1$ //$NON-NLS-2$
+            break;
+        case RIGHT :
+            width = s.insets.right;
+            y = s.y;
+            padding.right = p.right;
+            padding.left = s.innerPadding.right;
+            padding.top = padding.bottom = 0;
+            setParent(s.getParent());
+            data = stringcast(Format("right({})", s )); //$NON-NLS-1$ //$NON-NLS-2$
+            break;
+        case TOP :
+            height = s.insets.top;
+            //$TODO width of head/tail should be 0
+            width = 5;
+            padding.top = p.top;
+            padding.bottom = s.innerPadding.top;
+            padding.left = padding.right = 0;
+            data = stringcast(Format("top({})", s )); //$NON-NLS-1$ //$NON-NLS-2$
+            break;
+        case BOTTOM :
+            height = s.insets.bottom;
+            //$TODO width of head/tail should be 0
+            width = 5;
+            padding.top = s.innerPadding.bottom;
+            padding.bottom = p.bottom;
+            padding.left = padding.right = 0;
+            data = stringcast(Format("bottom({})", s )); //$NON-NLS-1$ //$NON-NLS-2$
+            break;
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/TightSpanningTreeSolver.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,178 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.TightSpanningTreeSolver;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.graph.SpanningTreeVisitor;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.Edge;
+import dwtx.draw2d.graph.EdgeList;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.NodeList;
+
+/**
+ * Finds a tight spanning tree from the graphs edges which induce a valid rank assignment.
+ * This process requires that the nodes be initially given a feasible ranking.
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+class TightSpanningTreeSolver : SpanningTreeVisitor {
+
+protected DirectedGraph graph;
+protected CandidateList candidates;
+
+static final class CandidateList {
+    private Edge[] edges;
+    private int size_;
+
+    public this(){
+        edges = new Edge[10];
+    }
+    public void add(Edge edge) {
+        if (size_ is edges.length - 1) {
+            Edge newEdges[] = new Edge[edges.length * 2];
+            System.arraycopy(edges, 0, newEdges, 0, edges.length);
+            edges = newEdges;
+        }
+        edges[size_++] = edge;
+    }
+
+    public Edge getEdge(int index) {
+        return edges[index];
+    }
+
+    public void remove(Edge edge) {
+        for (int i = 0; i < size_; i++) {
+            if (edges[i] is edge) {
+                edges[i] = edges[size_ - 1];
+                size_--;
+                return;
+            }
+        }
+        throw new RuntimeException("Remove called on invalid Edge"); //$NON-NLS-1$
+    }
+
+    public int size() {
+        return size_;
+    }
+}
+
+protected NodeList members;
+
+public this(){
+    candidates = new CandidateList();
+    members = new NodeList();
+}
+
+public void visit(DirectedGraph graph) {
+    this.graph = graph;
+    init();
+    solve();
+}
+
+Node addEdge(Edge edge) {
+    int delta = edge.getSlack();
+    edge.tree = true;
+    Node node;
+    if (edge.target.flag) {
+        delta = -delta;
+        node = edge.source;
+        setParentEdge(node, edge);
+        getSpanningTreeChildren(edge.target).add(edge);
+    } else {
+        node = edge.target;
+        setParentEdge(node, edge);
+        getSpanningTreeChildren(edge.source).add(edge);
+    }
+    members.adjustRank(delta);
+    addNode(node);
+    return node;
+}
+
+private bool isNodeReachable(Node node) {
+    return node.flag;
+}
+
+private void setNodeReachable(Node node) {
+    node.flag = true;
+}
+
+private bool isCandidate(Edge e) {
+    return e.flag;
+}
+
+private void setCandidate(Edge e) {
+    e.flag = true;
+}
+
+void addNode(Node node) {
+    setNodeReachable(node);
+    EdgeList list = node.incoming;
+    Edge e;
+    for (int i = 0; i < list.size(); i++) {
+        e = list.getEdge(i);
+        if (!isNodeReachable(e.source)) {
+            if (!isCandidate(e)) {
+                setCandidate(e);
+                candidates.add(e);
+            }
+        } else
+            candidates.remove(e);
+    }
+
+    list = node.outgoing;
+    for (int i = 0; i < list.size(); i++) {
+        e = list.getEdge(i);
+        if (!isNodeReachable(e.target)) {
+            if (!isCandidate(e)) {
+                setCandidate(e);
+                candidates.add(e);
+            }
+        } else
+            candidates.remove(e);
+    }
+    members.add(node);
+}
+
+void init() {
+    graph.edges.resetFlags(true);
+    graph.nodes.resetFlags();
+    for (int i = 0; i < graph.nodes.size(); i++) {
+        Node node = cast(Node)graph.nodes.get(i);
+        node.workingData[0] = new EdgeList();
+    }
+}
+
+protected void solve() {
+    Node root = graph.nodes.getNode(0);
+    setParentEdge(root, null);
+    addNode(root);
+    while (members.size() < graph.nodes.size()) {
+        if (candidates.size() is 0)
+            throw new RuntimeException("graph is not fully connected");//$NON-NLS-1$
+        int minSlack = Integer.MAX_VALUE, slack;
+        Edge minEdge = null, edge;
+        for (int i = 0; i < candidates.size() && minSlack > 0; i++) {
+            edge = candidates.getEdge(i);
+            slack = edge.getSlack();
+            if (slack < minSlack) {
+                minSlack = slack;
+                minEdge = edge;
+            }
+        }
+        addEdge(minEdge);
+    }
+    graph.nodes.normalizeRanks();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/TransposeMetrics.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.graph.TransposeMetrics;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.PositionConstants;
+import dwtx.draw2d.geometry.Transposer;
+import dwtx.draw2d.graph.GraphVisitor;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.Edge;
+import dwtx.draw2d.graph.VirtualNode;
+
+class TransposeMetrics : GraphVisitor {
+
+Transposer t;
+
+public this(){
+    t = new Transposer();
+}
+
+public void visit(DirectedGraph g) {
+    if (g.getDirection() is PositionConstants.SOUTH)
+        return;
+    t.setEnabled(true);
+    int temp;
+    g.setDefaultPadding(t.t(g.getDefaultPadding()));
+    for (int i = 0; i < g.nodes.size(); i++) {
+        Node node = g.nodes.getNode(i);
+        temp = node.width;
+        node.width = node.height;
+        node.height = temp;
+        if (node.getPadding() !is null)
+            node.setPadding(t.t(node.getPadding()));
+    }
+}
+
+public void revisit(DirectedGraph g) {
+    if (g.getDirection() is PositionConstants.SOUTH)
+        return;
+    int temp;
+    g.setDefaultPadding(t.t(g.getDefaultPadding()));
+    for (int i = 0; i < g.nodes.size(); i++) {
+        Node node = cast(Node)g.nodes.get(i);
+        temp = node.width;
+        node.width = node.height;
+        node.height = temp;
+        temp = node.y;
+        node.y = node.x;
+        node.x = temp;
+        if (node.getPadding() !is null)
+            node.setPadding(t.t(node.getPadding()));
+    }
+    for (int i = 0; i < g.edges.size(); i++) {
+        Edge edge = g.edges.getEdge(i);
+        edge.start.transpose();
+        edge.end.transpose();
+        edge.getPoints().transpose();
+        List bends = edge.vNodes;
+        if (bends is null)
+            continue;
+        for (int b = 0; b < bends.size(); b++) {
+            VirtualNode vnode = cast(VirtualNode) bends.get(b);
+            temp = vnode.y;
+            vnode.y = vnode.x;
+            vnode.x = temp;
+            temp = vnode.width;
+            vnode.width = vnode.height;
+            vnode.height = temp;
+        }
+    }
+    g.size.transpose();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/Vertex.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,224 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.Vertex;
+
+import tango.text.convert.Format;
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.PositionConstants;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.graph.Obstacle;
+import dwtx.draw2d.graph.Path;
+import dwtx.draw2d.graph.Segment;
+
+/**
+ * A vertex representation for the ShortestPathRouting. Vertices are either one of
+ * four corners on an <code>Obstacle</code>(Rectangle), or one of the two end points of a
+ * <code>Path</code>.
+ *
+ * This class is not intended to be subclassed.
+ * @author Whitney Sorenson
+ * @since 3.0
+ */
+class Vertex
+    : Point
+{
+
+// constants for the vertex type
+static const int NOT_SET = 0;
+static const int INNIE = 1;
+static const int OUTIE = 2;
+
+// for shortest path
+List neighbors;
+bool isPermanent = false;
+Vertex label;
+double cost = 0;
+
+// for routing
+int nearestObstacle = 0;
+double offset = 0;
+int type = NOT_SET;
+int count = 0;
+int totalCount = 0;
+Obstacle obs;
+List paths;
+bool nearestObstacleChecked = false;
+Map cachedCosines;
+int positionOnObstacle = -1;
+
+private int origX, origY;
+
+/**
+ * Creates a new Vertex with the given x, y position and on the given obstacle.
+ *
+ * @param x x point
+ * @param y y point
+ * @param obs obstacle - can be null
+ */
+this(int x, int y, Obstacle obs) {
+    super(x, y);
+    origX = x;
+    origY = y;
+    this.obs = obs;
+}
+
+/**
+ * Creates a new Vertex with the given point position and on the given obstacle.
+ *
+ * @param p the point
+ * @param obs obstacle - can be null
+ */
+this(Point p, Obstacle obs) {
+    this(p.x, p.y, obs);
+}
+
+/**
+ * Adds a path to this vertex, calculates angle between two segments and caches it.
+ *
+ * @param path the path
+ * @param start the segment to this vertex
+ * @param end the segment away from this vertex
+ */
+void addPath(Path path, Segment start, Segment end) {
+    if (paths is null) {
+        paths = new ArrayList();
+        cachedCosines = new HashMap();
+    }
+    if (!paths.contains(path))
+        paths.add(path);
+    cachedCosines.put(path, new Double(start.cosine(end)));
+}
+
+/**
+ * Creates a point that represents this vertex offset by the given amount times
+ * the offset.
+ *
+ * @param modifier the offset
+ * @return a Point that has been bent around this vertex
+ */
+Point bend(int modifier) {
+    Point point = new Point(x, y);
+    if ((positionOnObstacle & PositionConstants.NORTH) > 0)
+        point.y -= modifier * offset;
+    else
+        point.y += modifier * offset;
+    if ((positionOnObstacle & PositionConstants.EAST) > 0)
+        point.x += modifier * offset;
+    else
+        point.x -= modifier * offset;
+    return point;
+}
+
+/**
+ * Resets all fields on this Vertex.
+ */
+void fullReset() {
+    totalCount = 0;
+    type = NOT_SET;
+    count = 0;
+    cost = 0;
+    offset = getSpacing();
+    nearestObstacle = 0;
+    label = null;
+    nearestObstacleChecked = false;
+    isPermanent = false;
+    if (neighbors !is null)
+        neighbors.clear();
+    if (cachedCosines !is null)
+        cachedCosines.clear();
+    if (paths !is null)
+        paths.clear();
+}
+
+/**
+ * Returns a Rectangle that represents the region around this vertex that
+ * paths will be traveling in.
+ *
+ * @param extraOffset a buffer to add to the region.
+ * @return the rectangle
+ */
+Rectangle getDeformedRectangle(int extraOffset) {
+    Rectangle rect = new Rectangle(0, 0, 0, 0);
+
+    if ((positionOnObstacle & PositionConstants.NORTH) > 0) {
+        rect.y = y - extraOffset;
+        rect.height = origY - y + extraOffset;
+    } else {
+        rect.y = origY;
+        rect.height = y - origY + extraOffset;
+    }
+    if ((positionOnObstacle & PositionConstants.EAST) > 0) {
+        rect.x = origX;
+        rect.width = x - origX + extraOffset;
+    } else {
+        rect.x = x - extraOffset;
+        rect.width = origX - x + extraOffset;
+    }
+
+    return rect;
+}
+
+private int getSpacing() {
+    if (obs is null)
+        return 0;
+    return obs.getSpacing();
+}
+
+/**
+ * Grows this vertex by its offset to its maximum size.
+ */
+void grow() {
+    int modifier;
+
+    if (nearestObstacle is 0)
+        modifier = totalCount * getSpacing();
+    else
+        modifier = (nearestObstacle / 2) - 1;
+
+    if ((positionOnObstacle & PositionConstants.NORTH) > 0)
+        y -= modifier;
+    else
+        y += modifier;
+    if ((positionOnObstacle & PositionConstants.EAST) > 0)
+        x += modifier;
+    else
+        x -= modifier;
+}
+
+/**
+ * Shrinks this vertex to its original size.
+ */
+void shrink() {
+    x = origX;
+    y = origY;
+}
+
+/**
+ * Updates the offset of this vertex based on its shortest distance.
+ */
+void updateOffset() {
+    if (nearestObstacle !is 0)
+        offset = ((nearestObstacle / 2) - 1) / totalCount;
+}
+
+/**
+ * @see dwtx.draw2d.geometry.Point#toString()
+ */
+public String toString() {
+    return Format("V({}, {})", origX, origY ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/VerticalPlacement.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.VerticalPlacement;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.graph.GraphVisitor;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.Rank;
+import dwtx.draw2d.graph.Node;
+
+/**
+ * Assigns the Y and Height values to the nodes in the graph. All nodes in the same row
+ * are given the same height.
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+class VerticalPlacement : GraphVisitor {
+
+void visit(DirectedGraph g) {
+    Insets pad;
+    int currentY = g.getMargin().top;
+    int row, rowHeight;
+    g.rankLocations = new int[g.ranks.size() + 1];
+    for (row = 0; row < g.ranks.size(); row++) {
+        g.rankLocations[row] = currentY;
+        Rank rank = g.ranks.getRank(row);
+        rowHeight = 0;
+        rank.topPadding = rank.bottomPadding = 0;
+        for (int n = 0; n < rank.size(); n++) {
+            Node node = rank.getNode(n);
+            pad = g.getPadding(node);
+            rowHeight = Math.max(node.height, rowHeight);
+            rank.topPadding = Math.max(pad.top, rank.topPadding);
+            rank.bottomPadding = Math.max(pad.bottom, rank.bottomPadding);
+        }
+        currentY += rank.topPadding;
+        rank.setDimensions(currentY, rowHeight);
+        currentY += rank.height + rank.bottomPadding;
+    }
+    g.rankLocations[row] = currentY;
+    g.size.height = currentY;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/VirtualNode.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.VirtualNode;
+
+import dwt.dwthelper.utils;
+import tango.text.convert.Format;
+
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.Edge;
+import dwtx.draw2d.graph.Subgraph;
+
+/**
+ * @deprecated virtual nodes of an edge should be cast to Node.
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+public class VirtualNode : Node {
+
+/**
+ * The next node.
+ */
+public Node next;
+
+/**
+ * The previous node.
+ */
+public Node prev;
+
+/**
+ * Constructs a virtual node.
+ * @deprecated This class is for internal use only.
+ * @param e the edge
+ * @param i the row
+ */
+public this(Edge e, int i) {
+    super(e);
+    incoming.add(e);
+    outgoing.add(e);
+    width = e.width;
+    height = 0;
+    rank = i;
+    setPadding(new Insets(0, e.padding, 0, e.padding));
+}
+
+/**
+ * Constructor.
+ * @param o object
+ * @param parent subgraph
+ */
+public this(Object o, Subgraph parent) {
+    super(o, parent);
+}
+
+/**
+ * Returns the index of {@link #prev}.
+ * @return median
+ */
+public double medianIncoming() {
+    return prev.index;
+}
+
+/**
+ * Returns the index of {@link #next}.
+ * @return outgoing
+ */
+public double medianOutgoing() {
+    return next.index;
+}
+
+/**
+ * For internal use only.  Returns the original edge weight multiplied by the omega value
+ * for the this node and the node on the previous rank.
+ * @return the weighted weight, or omega
+ */
+public int omega() {
+    Edge e = cast(Edge)data;
+    if (e.source.rank + 1 < rank  && rank < e.target.rank)
+        return 8 * e.weight;
+    return 2 * e.weight;
+}
+
+/**
+ * @see java.lang.Object#toString()
+ */
+public String toString() {
+    if (auto edge = cast(Edge)data )
+        return Format("VN[{}]({})", (edge.vNodes.indexOf(this) + 1) //$NON-NLS-1$
+                , data ); //$NON-NLS-1$ //$NON-NLS-2$
+    return super.toString();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/VirtualNodeCreation.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.graph.VirtualNodeCreation;
+
+import dwt.dwthelper.utils;
+import tango.text.convert.Format;
+
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.graph.Edge;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.RevertableChange;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.Subgraph;
+import dwtx.draw2d.graph.VirtualNode;
+import dwtx.draw2d.graph.GraphUtilities;
+import dwtx.draw2d.graph.NodeList;
+
+/**
+ * Encapsulates the conversion of a long edge to multiple short edges and back.
+ * @since 3.1
+ */
+class VirtualNodeCreation : RevertableChange {
+
+private const Edge edge;
+private const DirectedGraph graph;
+private Node nodes[];
+private Edge[] edges;
+
+private static const int INNER_EDGE_X = 2;
+private static const int LONG_EDGE_X = 8;
+
+/**
+ * Breaks a single edge into multiple edges containing virtual nodes.
+ * @since 3.1
+ * @param edge The edge to convert
+ * @param graph the graph containing the edge
+ */
+public this(Edge edge, DirectedGraph graph) {
+    this.edge = edge;
+    this.graph = graph;
+
+    int size = edge.target.rank - edge.source.rank - 1;
+    int offset = edge.source.rank + 1;
+
+    Node prevNode = edge.source;
+    Node currentNode;
+    Edge currentEdge;
+    nodes = new Node[size];
+    edges = new Edge[size + 1];
+
+    Insets padding = new Insets(0, edge.padding, 0, edge.padding);
+
+    Subgraph s = GraphUtilities.getCommonAncestor(edge.source, edge.target);
+
+    for (int i = 0; i < size; i++) {
+        nodes[i] = currentNode = new VirtualNode(stringcast(Format("Virtual{}:{}", i, edge)), s); //$NON-NLS-1$
+        currentNode.width = edge.width;
+        if (s !is null) {
+            currentNode.nestingIndex = s.nestingIndex;
+        }
+
+        currentNode.height = 0;
+        currentNode.setPadding(padding);
+        currentNode.rank = offset + i;
+        graph.ranks.getRank(offset + i).add(currentNode);
+
+        currentEdge = new Edge(prevNode, currentNode, 1, edge.weight * LONG_EDGE_X);
+        if (i is 0) {
+            currentEdge.weight = edge.weight * INNER_EDGE_X;
+            currentEdge.offsetSource = edge.offsetSource;
+        }
+        graph.edges.add(edges[i] = currentEdge);
+        graph.nodes.add(currentNode);
+        prevNode = currentNode;
+    }
+
+    currentEdge = new Edge(prevNode, edge.target, 1, edge.weight * INNER_EDGE_X);
+    currentEdge.offsetTarget = edge.offsetTarget;
+    graph.edges.add(edges[edges.length - 1] = currentEdge);
+    graph.removeEdge(edge);
+}
+
+void revert() {
+    edge.start = edges[0].start;
+    edge.end = edges[edges.length - 1].end;
+    edge.vNodes = new NodeList();
+    for (int i = 0; i < edges.length; i++) {
+        graph.removeEdge(edges[i]);
+    }
+    for (int i = 0; i < nodes.length; i++) {
+        edge.vNodes.add(nodes[i]);
+        graph.removeNode(nodes[i]);
+    }
+    edge.source.outgoing.add(edge);
+    edge.target.incoming.add(edge);
+
+    graph.edges.add(edge);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/internal/MultiValueMap.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.internal.MultiValueMap;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+public class MultiValueMap {
+    private HashMap map;
+
+    public this(){
+        map = new HashMap();
+    }
+
+    public ArrayList get(Object key) {
+        Object value = map.get(key);
+        if (value is null) return null;
+
+        if (auto r = cast(ArrayList)value )
+            return r;
+        ArrayList v = new ArrayList(1);
+        v.add(value);
+        return v;
+    }
+
+
+
+    public void put(Object key, Object value) {
+        Object existingValues = map.get(key);
+        if (existingValues is null) {
+            map.put(key, value);
+            return;
+        }
+        if (auto v = cast(ArrayList)existingValues ) {
+            if (!v.contains(value))
+                v.add(value);
+            return;
+        }
+        if (existingValues !is value) {
+            ArrayList v = new ArrayList(2);
+            v.add(existingValues);
+            v.add(value);
+            map.put(key, v);
+        }
+    }
+
+    public int remove(Object key, Object value) {
+        Object existingValues = map.get(key);
+        if (existingValues !is null) {
+            if (auto v = cast(ArrayList)existingValues ) {
+                int result = v.indexOf(value);
+                if (result is -1)
+                    return -1;
+                v.remove(result);
+                if (v.isEmpty())
+                    map.remove(key);
+                return result;
+            }
+            if (map.remove(key) !is null)
+                return 0;
+        }
+        return -1;
+    }
+
+    public Object removeValue(Object value) {
+        Iterator iter = map.values().iterator();
+        Object current;
+        while (iter.hasNext()) {
+            current = iter.next();
+            if (value.opEquals(current)) {
+                iter.remove();
+                return value;
+            } else if (auto curlist = cast(List)current ) {
+                if (curlist.remove(value)) {
+                    if (curlist.isEmpty())
+                        iter.remove();
+                    return value;
+                }
+            }
+        }
+        return null;
+    }
+
+    public int size() {
+        return map.size();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/parts/ScrollableThumbnail.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,284 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.parts.ScrollableThumbnail;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Bean;
+
+import dwt.DWT;
+import dwt.graphics.Image;
+import dwt.graphics.ImageData;
+import dwt.graphics.PaletteData;
+import dwt.graphics.RGB;
+import dwt.widgets.Display;
+import dwtx.draw2d.ColorConstants;
+import dwtx.draw2d.Figure;
+import dwtx.draw2d.FigureListener;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.KeyEvent;
+import dwtx.draw2d.KeyListener;
+import dwtx.draw2d.MouseEvent;
+import dwtx.draw2d.MouseListener;
+import dwtx.draw2d.MouseMotionListener;
+import dwtx.draw2d.Viewport;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.parts.Thumbnail;
+
+/**
+ * A scaled image representation of a Figure.  If the source Figure is not completely
+ * visible, a SelectorFigure is placed over the thumbnail representing the viewable area
+ * and can be dragged around to scroll the source figure.
+ */
+public final class ScrollableThumbnail
+    : Thumbnail
+{
+
+private class ClickScrollerAndDragTransferrer
+    : MouseMotionListener.Stub
+    , MouseListener
+{
+    private bool dragTransfer;
+    public void mouseDoubleClicked(MouseEvent me) { }
+    public void mouseDragged(MouseEvent me) {
+        if (dragTransfer)
+            syncher.mouseDragged(me);
+    }
+    public void mousePressed(MouseEvent me) {
+        if (!(this.outer.getClientArea().contains(me.getLocation())))
+            return;
+        Dimension selectorCenter = selector.getBounds().getSize().scale(0.5f);
+        Point scrollPoint = me.getLocation()
+                            .getTranslated(getLocation().getNegated())
+                            .translate(selectorCenter.negate())
+                            .scale(1.0f / getViewportScaleX(), 1.0f / getViewportScaleY())
+                            .translate(
+                                    viewport.getHorizontalRangeModel().getMinimum(),
+                                    viewport.getVerticalRangeModel().getMinimum());
+        viewport.setViewLocation(scrollPoint);
+        syncher.mousePressed(me);
+        dragTransfer = true;
+    }
+    public void mouseReleased(MouseEvent me) {
+        syncher.mouseReleased(me);
+        dragTransfer = false;
+    }
+}
+
+private class ScrollSynchronizer
+    : MouseMotionListener.Stub
+    , MouseListener
+{
+    private Point startLocation;
+    private Point viewLocation;
+
+    public void mouseDoubleClicked(MouseEvent me) { }
+
+    public void mouseDragged(MouseEvent me) {
+        Dimension d = me.getLocation().getDifference(startLocation);
+        d.scale(1.0f / getViewportScaleX(), 1.0f / getViewportScaleY());
+        viewport.setViewLocation(viewLocation.getTranslated(d));
+        me.consume();
+    }
+
+    public void mousePressed(MouseEvent me) {
+        startLocation = me.getLocation();
+        viewLocation = viewport.getViewLocation();
+        me.consume();
+    }
+
+    public void mouseReleased(MouseEvent me) { }
+}
+
+private class SelectorFigure
+    : Figure
+{
+    this(){
+        iBounds = new Rectangle(0, 0, 1, 1);
+        Display display = Display.getCurrent();
+        PaletteData pData = new PaletteData(0xFF, 0xFF00, 0xFF0000);
+        RGB rgb = ColorConstants.menuBackgroundSelected.getRGB();
+        int fillColor = pData.getPixel(rgb);
+        ImageData iData = new ImageData(1, 1, 24, pData);
+        iData.setPixel(0, 0, fillColor);
+        iData.setAlpha(0, 0, 55);
+        image = new Image(display, iData);
+    }
+    private Rectangle iBounds;
+
+    private Image image;
+
+    protected void dispose() {
+        image.dispose();
+    }
+
+    public void paintFigure(Graphics g) {
+        Rectangle bounds = getBounds().getCopy();
+
+        // Avoid drawing images that are 0 in dimension
+        if (bounds.width < 5 || bounds.height < 5)
+            return;
+
+        // Don't paint the selector figure if the entire source is visible.
+        Dimension thumbnailSize = new Dimension(getThumbnailImage());
+        // expand to compensate for rounding errors in calculating bounds
+        Dimension size = getSize().getExpanded(1, 1);
+        if (size.contains(thumbnailSize))
+            return;
+
+        bounds.height--;
+        bounds.width--;
+        g.drawImage(image, iBounds, bounds);
+
+        g.setForegroundColor(ColorConstants.menuBackgroundSelected);
+        g.drawRectangle(bounds);
+    }
+
+}
+private FigureListener figureListener;
+private void initFigureListener(){
+    figureListener = new class() FigureListener {
+        public void figureMoved(IFigure source) {
+            reconfigureSelectorBounds();
+        }
+    };
+}
+private KeyListener keyListener;
+private void initKeyListener(){
+    keyListener = new class() KeyListenerStub {
+        public void keyPressed(KeyEvent ke) {
+            int moveX = viewport.getClientArea().width / 4;
+            int moveY = viewport.getClientArea().height / 4;
+            if (ke.keycode is DWT.HOME || (isMirrored() ? ke.keycode is DWT.ARROW_RIGHT
+                    : ke.keycode is DWT.ARROW_LEFT))
+                viewport.setViewLocation(viewport.getViewLocation().translate(-moveX, 0));
+            else if (ke.keycode is DWT.END || (isMirrored() ? ke.keycode is DWT.ARROW_LEFT
+                    : ke.keycode is DWT.ARROW_RIGHT))
+                viewport.setViewLocation(viewport.getViewLocation().translate(moveX, 0));
+            else if (ke.keycode is DWT.ARROW_UP || ke.keycode is DWT.PAGE_UP)
+                viewport.setViewLocation(viewport.getViewLocation().translate(0, -moveY));
+            else if (ke.keycode is DWT.ARROW_DOWN  || ke.keycode is DWT.PAGE_DOWN)
+                viewport.setViewLocation(viewport.getViewLocation().translate(0, moveY));
+        }
+    };
+}
+
+private PropertyChangeListener propListener;
+private void initPropListener(){
+    propListener = new class() PropertyChangeListener {
+        public void propertyChange(PropertyChangeEvent evt) {
+            reconfigureSelectorBounds();
+        }
+    };
+}
+
+private SelectorFigure selector;
+
+private ScrollSynchronizer syncher;
+private Viewport viewport;
+
+/**
+ * Creates a new ScrollableThumbnail.
+ */
+public this() {
+    super();
+    initFigureListener();
+    initKeyListener();
+    initPropListener();
+    initialize();
+}
+
+/**
+ * Creates a new ScrollableThumbnail that synchs with the given Viewport.
+ * @param port The Viewport
+ */
+public this(Viewport port) {
+    super();
+    initFigureListener();
+    initKeyListener();
+    initPropListener();
+    setViewport(port);
+    initialize();
+}
+
+/**
+ * @see Thumbnail#deactivate()
+ */
+public void deactivate() {
+    viewport.removePropertyChangeListener(Viewport.PROPERTY_VIEW_LOCATION, propListener);
+    viewport.removeFigureListener(figureListener);
+    remove(selector);
+    selector.dispose();
+    super.deactivate();
+}
+
+private double getViewportScaleX() {
+    return cast(double)targetSize.width / viewport.getContents().getBounds().width;
+}
+
+private double getViewportScaleY() {
+    return cast(double)targetSize.height / viewport.getContents().getBounds().height;
+}
+
+private void initialize() {
+    selector = new SelectorFigure();
+    selector.addMouseListener(syncher = new ScrollSynchronizer());
+    selector.addMouseMotionListener(syncher);
+    selector.setFocusTraversable(true);
+    selector.addKeyListener(keyListener);
+    add(selector);
+    ClickScrollerAndDragTransferrer transferrer =
+                new ClickScrollerAndDragTransferrer();
+    addMouseListener(transferrer);
+    addMouseMotionListener(transferrer);
+}
+
+private void reconfigureSelectorBounds() {
+    Rectangle rect = new Rectangle();
+    Point offset = viewport.getViewLocation();
+    offset.x -= viewport.getHorizontalRangeModel().getMinimum();
+    offset.y -= viewport.getVerticalRangeModel().getMinimum();
+    rect.setLocation(offset);
+    rect.setSize(viewport.getClientArea().getSize());
+    rect.scale(getViewportScaleX(), getViewportScaleY());
+    rect.translate(getClientArea().getLocation());
+    selector.setBounds(rect);
+}
+
+/**
+ * Reconfigures the SelectorFigure's bounds if the scales have changed.
+ * @param scaleX The X scale
+ * @param scaleY The Y scale
+ * @see dwtx.draw2d.parts.Thumbnail#setScales(float, float)
+ */
+protected void setScales(float scaleX, float scaleY) {
+    if (scaleX is getScaleX() && scaleY is getScaleY())
+        return;
+
+    super.setScales(scaleX, scaleY);
+    reconfigureSelectorBounds();
+}
+
+/**
+ * Sets the Viewport that this ScrollableThumbnail will synch with.
+ * @param port The Viewport
+ */
+public void setViewport(Viewport port) {
+    port.addPropertyChangeListener(Viewport.PROPERTY_VIEW_LOCATION, propListener);
+    port.addFigureListener(figureListener);
+    viewport = port;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/parts/Thumbnail.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,513 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.parts.Thumbnail;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+import dwtx.dwtxhelper.Collection;
+
+import dwt.DWT;
+import dwt.graphics.Color;
+import dwt.graphics.GC;
+import dwt.graphics.Image;
+static import dwtx.draw2d.geometry.Point;
+import dwt.widgets.Display;
+import dwtx.draw2d.Figure;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.SWTGraphics;
+import dwtx.draw2d.ScaledGraphics;
+import dwtx.draw2d.UpdateListener;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Rectangle;
+
+/**
+ * A Thumbnail is a Figure that displays an image of its source Figure at a
+ * smaller size. The Thumbnail will maintain the aspect ratio of the source
+ * Figure.
+ *
+ * @author Eric Bordeau
+ */
+public class Thumbnail
+    : Figure
+    , UpdateListener
+{
+alias Figure.getPreferredSize getPreferredSize;
+
+
+/**
+ * This updates the Thumbnail by breaking the thumbnail {@link Image} into
+ * several tiles and updating each tile individually.
+ */
+class ThumbnailUpdater : Runnable {
+    static final int MAX_BUFFER_SIZE = 256;
+    private int currentHTile, currentVTile;
+    private int hTiles, vTiles;
+    private bool isActive_ = true;
+
+    private bool isRunning_ = false;
+    private GC thumbnailGC;
+    private ScaledGraphics thumbnailGraphics;
+    private Dimension tileSize;
+
+    /**
+     * Stops the updater and disposes of any resources.
+     */
+    public void deactivate() {
+        setActive(false);
+        stop();
+        if (thumbnailImage !is null) {
+            thumbnailImage.dispose();
+            thumbnailImage = null;
+            thumbnailImageSize = null;
+        }
+    }
+
+    /**
+     * Returns the current horizontal tile index.
+     * @return current horizontal tile index.
+     */
+    protected int getCurrentHTile() {
+        return currentHTile;
+    }
+
+    /**
+     * Returns the current vertical tile index.
+     * @return current vertical tile index.
+     */
+    protected int getCurrentVTile() {
+        return currentVTile;
+    }
+
+    /**
+     * Returns <code>true</code> if this ThumbnailUpdater is active.  An inactive
+     * updater has disposed of its {@link Image}.  The updater may be active and
+     * not currently running.
+     * @return <code>true</code> if this ThumbnailUpdater is active
+     */
+    public bool isActive() {
+        return isActive_;
+    }
+
+    /**
+     * Returns <code>true</code> if this is currently running and updating at
+     * least one tile on the thumbnail {@link Image}.
+     * @return <code>true</code> if this is currently running
+     */
+    public bool isRunning() {
+        return isRunning_;
+    }
+
+    /**
+     * Resets the number of vertical and horizontal tiles, as well as the tile
+     * size and current tile index.
+     */
+    public void resetTileValues() {
+        hTiles = cast(int)Math.ceil(cast(float)getSourceRectangle().width
+                                    / cast(float)MAX_BUFFER_SIZE);
+        vTiles = cast(int)Math.ceil(cast(float)getSourceRectangle().height
+                                    / cast(float)MAX_BUFFER_SIZE);
+
+        tileSize = new Dimension(cast(int)Math.ceil(cast(float)getSourceRectangle().width
+                                    / cast(float)hTiles),
+                                cast(int)Math.ceil(cast(float)getSourceRectangle().height
+                                    / cast(float)vTiles));
+
+        currentHTile = 0;
+        currentVTile = 0;
+    }
+
+    /**
+     * Restarts the updater.
+     */
+    public void restart() {
+        stop();
+        start();
+    }
+
+    /**
+     * Updates the current tile on the Thumbnail.  An area of the source Figure
+     * is painted to an {@link Image}.  That Image is then drawn on the
+     * Thumbnail.  Scaling of the source Image is done inside
+     * {@link GC#drawImage(Image, int, int, int, int, int, int, int, int)} since
+     * the source and target sizes are different.  The current tile indexes are
+     * incremented and if more updating is necesary, this {@link Runnable} is
+     * called again in a {@link Display#timerExec(int, Runnable)}.  If no more
+     * updating is required, {@link #stop()} is called.
+     */
+    public void run() {
+        if (!isActive() || !isRunning())
+            return;
+        int v = getCurrentVTile();
+        int sy1 = v * tileSize.height;
+        int sy2 = Math.min((v + 1) * tileSize.height, getSourceRectangle().height);
+
+        int h = getCurrentHTile();
+        int sx1 = h * tileSize.width;
+        int sx2 = Math.min((h + 1) * tileSize.width, getSourceRectangle().width);
+        dwtx.draw2d.geometry.Point.Point p = getSourceRectangle().getLocation();
+
+        Rectangle rect = new Rectangle(sx1 + p.x, sy1 + p.y, sx2 - sx1, sy2 - sy1);
+        thumbnailGraphics.pushState();
+        thumbnailGraphics.setClip(rect);
+        thumbnailGraphics.fillRectangle(rect);
+        sourceFigure.paint(thumbnailGraphics);
+        thumbnailGraphics.popState();
+
+        if (getCurrentHTile() < (hTiles - 1))
+            setCurrentHTile(getCurrentHTile() + 1);
+        else {
+            setCurrentHTile(0);
+            if (getCurrentVTile() < (vTiles - 1))
+                setCurrentVTile(getCurrentVTile() + 1);
+            else
+                setCurrentVTile(0);
+        }
+
+        if (getCurrentHTile() !is 0 || getCurrentVTile() !is 0)
+            Display.getCurrent().asyncExec(this);
+        else if (isDirty()) {
+            setDirty(false);
+            Display.getCurrent().asyncExec(this);
+            repaint();
+        } else {
+            stop();
+            repaint();
+        }
+    }
+
+    /**
+     * Sets the active flag.
+     * @param value The active value
+     */
+    public void setActive(bool value) {
+        isActive_ = value;
+    }
+
+    /**
+     * Sets the current horizontal tile index.
+     * @param count current horizontal tile index
+     */
+    protected void setCurrentHTile(int count) {
+        currentHTile = count;
+    }
+
+    /**
+     * Sets the current vertical tile index.
+     * @param count current vertical tile index
+     */
+    protected void setCurrentVTile(int count) {
+        currentVTile = count;
+    }
+
+    /**
+     * Starts this updater.  This method initializes all the necessary resources
+     * and puts this {@link Runnable} on the asynch queue.  If this updater is
+     * not active or is already running, this method just returns.
+     */
+    public void start() {
+        if (!isActive() || isRunning())
+            return;
+
+        isRunning_ = true;
+        setDirty(false);
+        resetTileValues();
+
+        if (!targetSize.opEquals(thumbnailImageSize)) {
+            resetThumbnailImage();
+        }
+
+        if (targetSize.isEmpty())
+            return;
+
+        thumbnailGC = new GC(thumbnailImage,
+                sourceFigure.isMirrored() ? DWT.RIGHT_TO_LEFT : DWT.NONE);
+        thumbnailGraphics = new ScaledGraphics(new SWTGraphics(thumbnailGC));
+        thumbnailGraphics.scale(getScaleX());
+        thumbnailGraphics.translate(getSourceRectangle().getLocation().negate());
+
+        Color color = sourceFigure.getForegroundColor();
+        if (color !is null)
+            thumbnailGraphics.setForegroundColor(color);
+        color = sourceFigure.getBackgroundColor();
+        if (color !is null)
+            thumbnailGraphics.setBackgroundColor(color);
+        thumbnailGraphics.setFont(sourceFigure.getFont());
+
+        setScales(targetSize.width / cast(float)getSourceRectangle().width,
+                 targetSize.height / cast(float)getSourceRectangle().height);
+
+        Display.getCurrent().asyncExec(this);
+    }
+
+    /**
+     *
+     * @since 3.2
+     */
+    private void resetThumbnailImage() {
+        if (thumbnailImage !is null)
+            thumbnailImage.dispose();
+
+        if (!targetSize.isEmpty()) {
+            thumbnailImage = new Image(Display.getDefault(),
+                    targetSize.width,
+                    targetSize.height);
+            thumbnailImageSize = new Dimension(targetSize);
+        }
+        else {
+            thumbnailImage = null;
+            thumbnailImageSize = new Dimension(0, 0);
+        }
+    }
+
+    /**
+     * Stops this updater.  Also disposes of resources (except the thumbnail
+     * image which is still needed for painting).
+     */
+    public void stop() {
+        isRunning_ = false;
+        if (thumbnailGC !is null) {
+            thumbnailGC.dispose();
+            thumbnailGC = null;
+        }
+        if (thumbnailGraphics !is null) {
+            thumbnailGraphics.dispose();
+            thumbnailGraphics = null;
+        }
+        // Don't dispose of the thumbnail image since it is needed to paint the
+        // figure when the source is not dirty (i.e. showing/hiding the dock).
+    }
+}
+private bool isDirty_;
+private float scaleX;
+private float scaleY;
+
+private IFigure sourceFigure;
+Dimension targetSize;
+private Image thumbnailImage;
+private Dimension thumbnailImageSize;
+private ThumbnailUpdater updater;
+
+/**
+ * Creates a new Thumbnail.  The source Figure must be set separately if you
+ * use this constructor.
+ */
+public this() {
+    super();
+    targetSize = new Dimension(0, 0);
+    updater = new ThumbnailUpdater();
+}
+
+/**
+ * Creates a new Thumbnail with the given IFigure as its source figure.
+ * @param fig The source figure
+ */
+public this(IFigure fig) {
+    this();
+    setSource(fig);
+}
+
+private Dimension adjustToAspectRatio(Dimension size, bool adjustToMaxDimension) {
+    Dimension sourceSize = getSourceRectangle().getSize();
+    Dimension borderSize = new Dimension(getInsets().getWidth(), getInsets().getHeight());
+    size.expand(borderSize.getNegated());
+    int width, height;
+    if (adjustToMaxDimension) {
+        width  = Math.max(size.width, cast(int)(size.height * sourceSize.width
+                                            / cast(float)sourceSize.height + 0.5));
+        height = Math.max(size.height, cast(int)(size.width * sourceSize.height
+                                            / cast(float)sourceSize.width + 0.5));
+    } else {
+        width  = Math.min(size.width,  cast(int)(size.height * sourceSize.width
+                                            / cast(float)sourceSize.height + 0.5));
+        height = Math.min(size.height, cast(int)(size.width * sourceSize.height
+                                            / cast(float)sourceSize.width + 0.5));
+    }
+    size.width  = width;
+    size.height = height;
+    return size.expand(borderSize);
+}
+
+/**
+ * Deactivates this Thumbnail.
+ */
+public void deactivate() {
+    sourceFigure.getUpdateManager().removeUpdateListener(this);
+    updater.deactivate();
+}
+
+/**
+ * Returns the preferred size of this Thumbnail.  The preferred size will be
+ * calculated in a way that maintains the source Figure's aspect ratio.
+ *
+ * @param wHint The width hint
+ * @param hHint The height hint
+ * @return The preferred size
+ */
+public Dimension getPreferredSize(int wHint, int hHint) {
+    if (prefSize is null)
+        return adjustToAspectRatio(getBounds().getSize(), false);
+
+    Dimension preferredSize = adjustToAspectRatio(prefSize.getCopy(), true);
+
+    if (maxSize is null)
+        return preferredSize;
+
+    Dimension maximumSize = adjustToAspectRatio(maxSize.getCopy(), true);
+    if (preferredSize.contains(maximumSize))
+        return maximumSize;
+    else
+        return preferredSize;
+}
+
+/**
+ * Returns the scale factor on the X-axis.
+ * @return X scale
+ */
+protected float getScaleX() {
+    return scaleX;
+}
+
+/**
+ * Returns the scale factor on the Y-axis.
+ * @return Y scale
+ */
+protected float getScaleY() {
+    return scaleY;
+}
+
+/**
+ * Returns the source figure being used to generate a thumbnail.
+ * @return the source figure
+ */
+protected IFigure getSource() {
+    return sourceFigure;
+}
+
+/**
+ * Returns the rectangular region relative to the source figure which will be the basis of
+ * the thumbnail.  The value may be returned by reference and should not be modified by
+ * the caller.
+ * @since 3.1
+ * @return the region of the source figure being used for the thumbnail
+ */
+protected Rectangle getSourceRectangle() {
+    return sourceFigure.getBounds();
+}
+
+/**
+ * Returns the scaled Image of the source Figure.  If the Image needs to be
+ * updated, the ThumbnailUpdater will notified.
+ *
+ * @return The thumbnail image
+ */
+protected Image getThumbnailImage() {
+    Dimension oldSize = targetSize;
+    targetSize = getPreferredSize();
+    targetSize.expand((new Dimension(getInsets().getWidth(),
+                                    getInsets().getHeight())).negate());
+    setScales(targetSize.width / cast(float)getSourceRectangle().width,
+             targetSize.height / cast(float)getSourceRectangle().height);
+    if ((isDirty()) && !updater.isRunning())
+        updater.start();
+    else if (oldSize !is null && !targetSize.opEquals(oldSize)) {
+        revalidate();
+        updater.restart();
+    }
+
+    return thumbnailImage;
+}
+
+/**
+ * Returns <code>true</code> if the source figure has changed.
+ * @return <code>true</code> if the source figure has changed
+ */
+protected bool isDirty() {
+    return isDirty_;
+}
+
+/**
+ * @see dwtx.draw2d.UpdateListener#notifyPainting(Rectangle, Map)
+ */
+public void notifyPainting(Rectangle damage, Map dirtyRegions) {
+    Iterator dirtyFigures = dirtyRegions.keySet().iterator();
+    while (dirtyFigures.hasNext()) {
+        IFigure current = cast(IFigure)dirtyFigures.next();
+        while (current !is null) {
+            if (current is getSource()) {
+                setDirty(true);
+                repaint();
+                return;
+            }
+            current = current.getParent();
+        }
+    }
+}
+
+/**
+ * @see dwtx.draw2d.UpdateListener#notifyValidating()
+ */
+public void notifyValidating() {
+//  setDirty(true);
+//  revalidate();
+}
+
+/**
+ * @see dwtx.draw2d.Figure#paintFigure(Graphics)
+ */
+protected void paintFigure(Graphics graphics) {
+    Image thumbnail = getThumbnailImage();
+    if (thumbnail is null)
+        return;
+    graphics.drawImage(thumbnail, getClientArea().getLocation());
+}
+
+/**
+ * Sets the dirty flag.
+ * @param value The dirty value
+ */
+public void setDirty(bool value) {
+    isDirty_ = value;
+}
+
+/**
+ * Sets the X and Y scales for the Thumbnail.  These scales represent the ratio
+ * between the source figure and the Thumbnail.
+ * @param x The X scale
+ * @param y The Y scale
+ */
+protected void setScales(float x, float y) {
+    scaleX = x;
+    scaleY = y;
+}
+
+/**
+ * Sets the source Figure.  Also sets the scales and creates the necessary
+ * update manager.
+ * @param fig The source figure
+ */
+public void setSource(IFigure fig) {
+    if (sourceFigure is fig)
+        return;
+    if (sourceFigure !is null)
+        sourceFigure.getUpdateManager().removeUpdateListener(this);
+    sourceFigure = fig;
+    if (sourceFigure !is null) {
+        setScales(cast(float)getSize().width / cast(float)getSourceRectangle().width,
+                cast(float)getSize().height / cast(float)getSourceRectangle().height);
+        sourceFigure.getUpdateManager().addUpdateListener(this);
+        repaint();
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/AbstractFlowBorder.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.AbstractFlowBorder;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.AbstractBorder;
+import dwtx.draw2d.Border;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.text.FlowBorder;
+import dwtx.draw2d.text.FlowFigure;
+
+/**
+ * A basis for implementing {@link FlowBorder}. Subclassing this class will possibly
+ * guarantee compatibility with future changes to the FlowBorder interface. This class
+ * also returns default values for many of the required methods as a convenience.
+ * @since 3.1
+ */
+public abstract class AbstractFlowBorder
+    : AbstractBorder
+    , FlowBorder
+{
+/**
+ * @see FlowBorder#getBottomMargin()
+ */
+public int getBottomMargin() {
+    return 0;
+}
+
+/**
+ * @see Border#getInsets(IFigure)
+ */
+public Insets getInsets(IFigure figure) {
+    return IFigure_NO_INSETS;
+}
+
+/**
+ * @see FlowBorder#getLeftMargin()
+ */
+public int getLeftMargin() {
+    return 0;
+}
+
+/**
+ * @see FlowBorder#getRightMargin()
+ */
+public int getRightMargin() {
+    return 0;
+}
+
+/**
+ * @see FlowBorder#getTopMargin()
+ */
+public int getTopMargin() {
+    return 0;
+}
+
+/**
+ * This method is not called on FlowBorders. For this reason it is
+ * implemented here and made <code>final</code> so that clients override the correct
+ * method.
+ * @param figure the figure
+ * @param graphics the graphics
+ * @param insets the insets
+ * @see FlowBorder#paint(FlowFigure, Graphics, Rectangle, int)
+ */
+public final void paint(IFigure figure, Graphics graphics, Insets insets) { }
+
+/**
+ * Subclasses should override this method to paint each box's border.
+ * @see FlowBorder#paint(FlowFigure, Graphics, Rectangle, int)
+ */
+public void paint(FlowFigure figure, Graphics g, Rectangle where, int sides) { }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/BidiChars.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.text.BidiChars;
+
+import dwt.dwthelper.utils;
+
+/**
+ * @since 3.1
+ */
+class BidiChars {
+
+static const dchar P_SEP = '\u2029';
+static const dchar ZWJ = '\u200d';
+static const dchar LRO = '\u202d';
+static const dchar RLO = '\u202e';
+static const dchar OBJ = '\ufffc';
+static const dchar LRE = '\u202a';
+static const dchar RLE = '\u202b';
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/BidiInfo.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.text.BidiInfo;
+
+import dwt.dwthelper.utils;
+
+/**
+ * This class is for INTERNAL use only.
+ * @since 3.1
+ */
+public class BidiInfo {
+
+/**
+ * Odd-sized array consisting of bidi levels, interleaved with the offsets at which levels
+ * change.
+ */
+public int[] levelInfo;
+
+/**
+ * Indicates if the ZWJ character needs to be prepended to text being rendered.
+ */
+public bool leadingJoiner;
+
+/**
+ * Indicates if the ZRJ character needs to be appended to the text being rendered..
+ */
+public bool trailingJoiner;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/BidiProcessor.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,262 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.BidiProcessor;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+//import dwtx.dwtxhelper.UBidi;
+
+import dwt.DWT;
+import dwt.graphics.TextLayout;
+import dwtx.draw2d.text.FlowFigure;
+import dwtx.draw2d.text.BidiInfo;
+import dwtx.draw2d.text.FlowUtilities;
+
+class Bidi{
+    public static bool requiresBidi( char[], int, int ){
+        return false;
+    }
+}
+
+/**
+ * A helper class for a BlockFlow that does Bidi evaluation of all the text in that block.
+ * <p>
+ * WARNING: This class is for INTERNAL use only.
+ * @author Pratik Shah
+ * @since 3.1
+ */
+public final class BidiProcessor {
+
+/*
+ * $TODO Workaround for Carbon.  AWT DLL cannot start properly on carbon.
+ * Waiting for bug 82104
+ */
+private static const bool isMacOS;
+static this(){
+    isMacOS = DWT.getPlatform().equals("carbon"); //$NON-NLS-1$
+    INSTANCE = new BidiProcessor();
+}
+
+/**
+ * A helper class to hold information about contributions made to this processor.
+ *
+ * @author Pratik Shah
+ * @since 3.1
+ */
+private static class BidiEntry {
+    int begin, end;
+    FlowFigure fig;
+    this(FlowFigure fig, int offset, int length) {
+        this.fig = fig;
+        this.begin = offset;
+        this.end = offset + length;
+    }
+}
+
+/**
+ * A singleton instance.
+ */
+public static const BidiProcessor INSTANCE;
+private StringBuffer bidiText;
+private List list;
+private int orientation = DWT.LEFT_TO_RIGHT;
+
+private this() {
+    list = new ArrayList();
+}
+
+/**
+ * Records a String contribution for this bidi context. Contributions are
+ * concatenated (in the order that they were contributed) to make the final
+ * String which will determine the bidi info for all contributors.
+ * @param fig the figure that is contributing the given text
+ * @param str the text contributed by the given figure
+ * @see #addControlChar(char)
+ */
+public void add(FlowFigure fig, String str) {
+    //We are currently tracking empty contributions ("")
+    list.add(new BidiEntry(fig, bidiText.length(), str.length));
+    bidiText.append(str);
+}
+
+/**
+ * Records a character contribution for this bidi context. Contributions are
+ * concatenated (in the order that they were contributed) to make the final
+ * String which will determine the bidi info for all contributors.
+ * @param fig the figure that is contributing the given text
+ * @param c the character being added
+ * @see #addControlChar(char)
+ */
+public void add(FlowFigure fig, dchar c) {
+    auto str = dcharToString(c);
+    list.add(new BidiEntry(fig, bidiText.length(), str.length));
+    bidiText.append(str);
+}
+
+/**
+ * This methods allows FlowFigures to contribute text that may effect the bidi evaluation,
+ * but is not text that is visible on the screen.  The bidi level of such text is
+ * reported back to the contributing figure.
+ *
+ * @param c the control character
+ */
+public void addControlChar(dchar c) {
+    bidiText.append(dcharToString(c));
+}
+
+/**
+ * Breaks the given int array into bidi levels for each figure based on their
+ * contributed text, and assigns those levels to each figure.  Also determines
+ * if shaping needs to occur between figures and sets the appendJoiner, prependJoiner
+ * accordingly.
+ *
+ * @param levels the calculated levels of all the text in the block
+ */
+private void assignResults(int[] levels) {
+    BidiEntry prevEntry = null, entry = null;
+    BidiInfo prevInfo = null, info = null;
+    int end = 2, start = 0;
+    for (int i = 0; i < list.size(); i++) {
+        entry = cast(BidiEntry)list.get(i);
+        info = new BidiInfo();
+
+        while (levels[end] < entry.end)
+            end += 2;
+
+        int levelInfo[];
+        if (end is start) {
+            levelInfo = new int[1];
+            if (prevInfo !is null)
+                levelInfo[0] = prevInfo.levelInfo[prevInfo.levelInfo.length - 1];
+            else
+                levelInfo[0] = (orientation is DWT.LEFT_TO_RIGHT) ? 0 : 1;
+        } else {
+            levelInfo = new int[end - start - 1];
+            System.arraycopy(levels, start + 1, levelInfo, 0, levelInfo.length);
+        }
+        for (int j = 1; j < levelInfo.length; j += 2)
+            levelInfo[j] -= entry.begin;
+        info.levelInfo = levelInfo;
+
+        // Compare current and previous for joiners, and commit previous BidiInfo.
+        if (prevEntry !is null) {
+            if (// if we started in the middle of a level run
+                    levels[start] < entry.begin
+                    // and the level run is odd
+                    && levels[start + 1] % 2 is 1
+                    // and the first character of this figure is Arabic
+                    && isJoiner(entry.begin)
+                    // and the last character of the previous figure was Arabic
+                    && isPrecedingJoiner(entry.begin))
+                prevInfo.trailingJoiner = info.leadingJoiner = true;
+            prevEntry.fig.setBidiInfo(prevInfo);
+        }
+        prevEntry = entry;
+        prevInfo = info;
+        if (entry.end is levels[end])
+            start = end;
+        else
+            start = end - 2;
+    }
+    if (entry !is null)
+        entry.fig.setBidiInfo(info);
+}
+
+private bool isJoiner(int begin) {
+    return begin < bidiText.length && isJoiningCharacter(bidiText.slice()[begin.. $].firstCodePoint());
+}
+
+/**
+ * @param the character to be evaluated
+ * @return <code>true</code> if the given character is Arabic or ZWJ
+ */
+private bool isJoiningCharacter(dchar c) {
+    return false;
+    //FIXME: DWT Missing functionality
+//     return Character.getDirectionality(c) is Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC
+//             || c is BidiChars.ZWJ;
+}
+
+private bool isPrecedingJoiner(int begin) {
+    return begin > 0 && isJoiningCharacter(bidiText.slice()[begin - 1..$].firstCodePoint());
+}
+
+/**
+ * Processes the contributed text, determines the Bidi levels, and assigns them to
+ * the FlowFigures that made thet contributions. This class is for INTERNAL use
+ * only. Shaping of visually contiguous Arabic characters that are split in different
+ * figures is also handled. This method will do nothing if the contributed text does not
+ * require Bidi  evaluation. All contributions are discarded at the end of this method.
+ */
+public void process() {
+    try {
+        if (bidiText.length is 0 || isMacOS)
+            return;
+        char[] chars = bidiText.slice().dup;
+
+        if (orientation !is DWT.RIGHT_TO_LEFT
+                && !Bidi.requiresBidi(chars, 0, chars.length - 1))
+            return;
+
+        int[] levels = new int[15];
+        TextLayout layout = FlowUtilities.getTextLayout();
+
+        layout.setOrientation(orientation);
+        layout.setText(bidiText.toString());
+        int j = 0, offset, prevLevel = -1;
+        for (offset = 0; offset < chars.length; offset++) {
+            int newLevel = layout.getLevel(offset);
+            if (newLevel !is prevLevel) {
+                if (j + 3 > levels.length) {
+                    int temp[] = levels;
+                    levels = new int[levels.length * 2 + 1];
+                    System.arraycopy(temp, 0, levels, 0, temp.length);
+                }
+                levels[j++] = offset;
+                levels[j++] = newLevel;
+                prevLevel = newLevel;
+            }
+        }
+        levels[j++] = offset;
+
+        if (j !is levels.length) {
+            int[] newLevels = new int[j];
+            System.arraycopy(levels, 0, newLevels, 0, j);
+            levels = newLevels;
+        }
+        assignResults(levels);
+
+        // reset the orientation of the layout, in case it was set to RTL
+        layout.setOrientation(DWT.LEFT_TO_RIGHT);
+    } finally {
+        //will cause the fields to be reset for the next string to be processed
+        //bidiText = null;
+        bidiText.clear();
+        list.clear();
+    }
+}
+
+/**
+ * Sets the paragraph embedding.  The given orientation will be used on TextLayout
+ * when determining the Bidi levels.
+ *
+ * @param newOrientation DWT.LEFT_TO_RIGHT or DWT.RIGHT_TO_LEFT
+ */
+public void setOrientation(int newOrientation) {
+    bidiText = new StringBuffer();
+    list.clear();
+    orientation = newOrientation;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/BlockBox.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.BlockBox;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.text.CompositeBox;
+import dwtx.draw2d.text.BlockFlow;
+import dwtx.draw2d.text.LineRoot;
+import dwtx.draw2d.text.FlowBox;
+
+/**
+ * A CompositeBox suitable for containing multiple LineBox fragments.
+ * @author hudsonr
+ * @since 2.1
+ */
+public class BlockBox
+    : CompositeBox
+{
+
+int height;
+private int y;
+BlockFlow owner;
+
+this(BlockFlow owner) {
+    this.owner = owner;
+}
+
+/**
+ * @see CompositeBox#add(FlowBox)
+ */
+public void add(FlowBox box) {
+    width = Math.max(width, box.getWidth());
+    height = Math.max(height, box.getBaseline() + box.getDescent());
+}
+
+/**
+ * @see FlowBox#containsPoint(int, int)
+ */
+public bool containsPoint(int x, int y) {
+    return true;
+}
+
+/**
+ * @see FlowBox#getAscent()
+ */
+public int getAscent() {
+    return 0;
+}
+
+/**
+ * @see FlowBox#getBaseline()
+ */
+public int getBaseline() {
+    return y;
+}
+
+int getBottomMargin() {
+    return owner.getBottomMargin();
+}
+
+/**
+ * @see FlowBox#getDescent()
+ */
+public int getDescent() {
+    return height;
+}
+
+/**
+ * @return Returns the height.
+ */
+public int getHeight() {
+    return height;
+}
+
+LineRoot getLineRoot() {
+    return null;
+}
+
+int getTopMargin() {
+    return owner.getTopMargin();
+}
+
+/**
+ * Sets the height.
+ * @param h The height
+ */
+public void setHeight(int h) {
+    height = h;
+}
+
+/**
+ * @see CompositeBox#setLineTop(int)
+ */
+public void setLineTop(int y) {
+    this.y = y;
+}
+
+Rectangle toRectangle() {
+    return new Rectangle(getX(), y, Math.max(getWidth(), recommendedWidth), height);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/BlockFlow.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,313 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.BlockFlow;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwt.DWT;
+import dwtx.draw2d.ColorConstants;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.PositionConstants;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.text.FlowFigure;
+import dwtx.draw2d.text.BlockBox;
+import dwtx.draw2d.text.BidiProcessor;
+import dwtx.draw2d.text.FlowFigureLayout;
+import dwtx.draw2d.text.BidiChars;
+import dwtx.draw2d.text.BlockFlowLayout;
+import dwtx.draw2d.text.FlowBorder;
+
+/**
+ * A <code>FlowFigure</code> represented by a single {@link BlockBox} containing one or
+ * more lines. A BlockFlow is a creator of LineBoxes, which its children require during
+ * layout. A BlockFlow can be thought of as a foundation for a paragraph.
+ * <P>
+ * BlockFlows must be parented by a <code>FlowFigure</code>. {@link FlowPage} can be
+ * used as a "root" block and can be parented by normal Figures.
+ * <P>
+ * Only {@link FlowFigure}s can be added to a BlockFlow.
+ * <P>
+ * WARNING: This class is not intended to be subclassed by clients.
+ * @author hudsonr
+ * @since 2.1
+ */
+public class BlockFlow
+    : FlowFigure
+{
+
+private const BlockBox blockBox;
+private int alignment = PositionConstants.NONE;
+private int orientation = DWT.NONE;
+private bool bidiValid;
+
+/**
+ * Constructs a new BlockFlow.
+ */
+public this() {
+    blockBox = createBlockBox();
+}
+
+/**
+ * BlockFlows contribute a paragraph separator so as to keep the Bidi state of the text
+ * on either side of this block from affecting each other.  Since each block is like a
+ * different paragraph, it does not contribute any actual text to its containing block.
+ *
+ * @see dwtx.draw2d.text.FlowFigure#contributeBidi(dwtx.draw2d.text.BidiProcessor)
+ */
+protected void contributeBidi(BidiProcessor proc) {
+    proc.addControlChar(BidiChars.P_SEP);
+}
+
+BlockBox createBlockBox() {
+    return new BlockBox(this);
+}
+
+/**
+ * @see dwtx.draw2d.text.FlowFigure#createDefaultFlowLayout()
+ */
+protected FlowFigureLayout createDefaultFlowLayout() {
+    return new BlockFlowLayout(this);
+}
+
+/**
+ * Returns the BlockBox associated with this.
+ * @return This BlockFlow's BlockBox
+ */
+protected BlockBox getBlockBox() {
+    return blockBox;
+}
+package BlockBox getBlockBox_package() {
+    return getBlockBox();
+}
+
+int getBottomMargin() {
+    int margin = 0;
+    if (auto border = cast(FlowBorder)getBorder() ) {
+        return border.getBottomMargin();
+    }
+    List children = getChildren();
+    int childIndex = children.size() - 1;
+    if (childIndex >= 0 && null !is cast(BlockFlow)children.get(childIndex) ) {
+        margin = Math.max(margin,
+                (cast(BlockFlow)children.get(childIndex)).getBottomMargin());
+    }
+    return margin;
+}
+
+/**
+ * Returns the effective horizontal alignment. This method will never return {@link
+ * PositionConstants#NONE}. If the value is none, it will return the inherited alignment.
+ * If no alignment was inherited, it will return the default alignment ({@link
+ * PositionConstants#LEFT}).
+ * @return the effective alignment
+ */
+public int getHorizontalAligment() {
+    if (alignment !is PositionConstants.NONE)
+        return alignment;
+    IFigure parent = getParent();
+    while (parent !is null && !( null !is cast(BlockFlow)parent ))
+        parent = parent.getParent();
+    if (parent !is null)
+        return (cast(BlockFlow)parent).getHorizontalAligment();
+    return PositionConstants.LEFT;
+}
+
+int getLeftMargin() {
+    if ( auto b = cast(FlowBorder)getBorder() )
+        return b.getLeftMargin();
+    return 0;
+}
+
+/**
+ * Returns the orientation set on this block.
+ * @return LTR, RTL or NONE
+ * @see #setOrientation(int)
+ * @since 3.1
+ */
+public int getLocalOrientation() {
+    return orientation;
+}
+
+/**
+ * Returns the horizontal alignment set on this block.
+ * @return LEFT, RIGHT, ALWAYS_LEFT, ALWAYS_RIGHT, NONE
+ * @see #setHorizontalAligment(int)
+ * @since 3.1
+ */
+public int getLocalHorizontalAlignment() {
+    return alignment;
+}
+
+/**
+ * Returns this block's Bidi orientation.  If none was set on this block, it
+ * will inherit the one from its containing block.  If there is no containing block, it
+ * will return the default orientation (DWT.RIGHT_TO_LEFT if mirrored; DWT.LEFT_TO_RIGHT
+ * otherwise).
+ *
+ * @return DWT.RIGHT_TO_LEFT or DWT.LEFT_TO_RIGHT
+ * @see #setOrientation(int)
+ * @since 3.1
+ */
+public int getOrientation() {
+    if (orientation !is DWT.NONE)
+        return orientation;
+    IFigure parent = getParent();
+    while (parent !is null && !(null !is cast(BlockFlow)parent ))
+        parent = parent.getParent();
+    if (parent !is null)
+        return (cast(BlockFlow)parent).getOrientation();
+    return isMirrored() ? DWT.RIGHT_TO_LEFT : DWT.LEFT_TO_RIGHT;
+}
+
+int getRightMargin() {
+    if (auto b = cast(FlowBorder)getBorder() )
+        return b.getRightMargin();
+    return 0;
+}
+
+int getTopMargin() {
+    int margin = 0;
+    if (auto border = cast(FlowBorder)getBorder() ) {
+        return border.getTopMargin();
+    }
+    List children = getChildren();
+    if (children.size() > 0 && null !is cast(BlockFlow)children.get(0) ) {
+        margin = Math.max(margin,
+                (cast(BlockFlow)children.get(0)).getTopMargin());
+    }
+    return margin;
+}
+
+/**
+ * @see dwtx.draw2d.Figure#paintBorder(dwtx.draw2d.Graphics)
+ */
+public void paintBorder(Graphics graphics) {
+    if ( auto b = cast(FlowBorder)getBorder() ) {
+        Rectangle where = getBlockBox().toRectangle();
+        where.crop(new Insets(getTopMargin(), getLeftMargin(),
+                getBottomMargin(), getRightMargin()));
+        (cast(FlowBorder)getBorder()).paint(this, graphics, where, DWT.LEAD | DWT.TRAIL);
+    } else
+        super.paintBorder(graphics);
+    if (selectionStart !is -1) {
+        graphics.restoreState();
+        graphics.setXORMode(true);
+        graphics.setBackgroundColor(ColorConstants.white);
+        graphics.fillRectangle(getBounds());
+    }
+}
+
+/**
+ * @see dwtx.draw2d.text.FlowFigure#postValidate()
+ */
+public void postValidate() {
+    Rectangle newBounds = getBlockBox().toRectangle();
+    newBounds.crop(new Insets(getTopMargin(), getLeftMargin(),
+            getBottomMargin(), getRightMargin()));
+    setBounds(newBounds);
+}
+
+/**
+ * @see FlowFigure#revalidate()
+ */
+public void revalidate() {
+    BlockFlowLayout layout = cast(BlockFlowLayout)getLayoutManager();
+    layout.blockContentsChanged();
+    super.revalidate();
+}
+
+/**
+ * A Block will invalidate the Bidi state of all its children, so that it is
+ * re-evaluated when this block is next validated.
+ * @see dwtx.draw2d.text.FlowFigure#revalidateBidi(dwtx.draw2d.IFigure)
+ */
+protected void revalidateBidi(IFigure origin) {
+    if (bidiValid) {
+        bidiValid = false;
+        revalidate();
+    }
+}
+
+/**
+ * Sets the horitontal aligment of the block. Valid values are:
+ * <UL>
+ *   <LI>{@link PositionConstants#NONE NONE} - (default) Alignment is inherited from
+ *   parent.  If a parent is not found then LEFT is used.</LI>
+ *   <LI>{@link PositionConstants#LEFT} - Alignment is with leading edge</LI>
+ *   <LI>{@link PositionConstants#RIGHT} - Alignment is with trailing edge</LI>
+ *   <LI>{@link PositionConstants#CENTER}</LI>
+ *   <LI>{@link PositionConstants#ALWAYS_LEFT} - Left, irrespective of orientation</LI>
+ *   <LI>{@link PositionConstants#ALWAYS_RIGHT} - Right, irrespective of orientation</LI>
+ * </UL>
+ * @param value the aligment
+ * @see #getHorizontalAligment()
+ */
+public void setHorizontalAligment(int value) {
+    value &= PositionConstants.LEFT | PositionConstants.CENTER | PositionConstants.RIGHT
+        | PositionConstants.ALWAYS_LEFT | PositionConstants.ALWAYS_RIGHT;
+    if (value is alignment)
+        return;
+    alignment = value;
+    revalidate();
+}
+
+/**
+ * Sets the orientation for this block.  Orientation can be one of:
+ * <UL>
+ *   <LI>{@link DWT#LEFT_TO_RIGHT}
+ *   <LI>{@link DWT#RIGHT_TO_LEFT}
+ *   <LI>{@link DWT#NONE} (default)
+ * </UL>
+ * <code>NONE</code> is used to indicate that orientation should be inherited from the
+ * encompassing block.
+ *
+ * @param orientation LTR, RTL or NONE
+ * @see #getOrientation()
+ * @since 3.1
+ */
+public void setOrientation(int orientation) {
+    orientation &= DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT;
+    if (this.orientation is orientation)
+        return;
+    this.orientation = orientation;
+    revalidateBidi(this);
+}
+
+/**
+ * @see dwtx.draw2d.Figure#useLocalCoordinates()
+ */
+protected bool useLocalCoordinates() {
+    return true;
+}
+
+/**
+ * Re-evaluate the Bidi state of all the fragments if it has been
+ * invalidated.
+ * @see dwtx.draw2d.IFigure#validate()
+ */
+public void validate() {
+    if (!bidiValid) {
+        BidiProcessor.INSTANCE.setOrientation(getOrientation());
+        if (getOrientation() is DWT.LEFT_TO_RIGHT && isMirrored())
+            BidiProcessor.INSTANCE.addControlChar(BidiChars.LRE);
+        super.contributeBidi(BidiProcessor.INSTANCE);
+        BidiProcessor.INSTANCE.process();
+        bidiValid = true;
+    }
+    super.validate();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/BlockFlowLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,251 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.BlockFlowLayout;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwt.DWT;
+import dwtx.draw2d.Figure;
+import dwtx.draw2d.PositionConstants;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.text.FlowContainerLayout;
+import dwtx.draw2d.text.BlockBox;
+import dwtx.draw2d.text.BlockFlow;
+import dwtx.draw2d.text.CompositeBox;
+import dwtx.draw2d.text.FlowFigure;
+import dwtx.draw2d.text.LineRoot;
+
+/**
+ * The layout for {@link BlockFlow} figures.
+ *
+ * <P>WARNING: This class is not intended to be subclassed by clients.
+ * @author hudsonr
+ * @since 2.1
+ */
+public class BlockFlowLayout
+    : FlowContainerLayout
+{
+
+BlockBox blockBox;
+bool blockInvalid = false;
+private bool continueOnSameLine = false;
+private CompositeBox previousLine = null;
+
+/**
+ * Creates a new BlockFlowLayout with the given BlockFlow.
+ * @param blockFlow the BlockFlow
+ */
+public this(BlockFlow blockFlow) {
+    super(blockFlow);
+}
+
+private void addBelowPreviousLine(CompositeBox line) {
+    if (previousLine is null)
+        line.setLineTop(line.getTopMargin());
+    else
+        line.setLineTop(previousLine.getBaseline() + previousLine.getDescent()
+                + Math.max(previousLine.getBottomMargin(), line.getTopMargin()));
+
+    int alignment = getBlockFlow().getHorizontalAligment();
+    if (alignment is PositionConstants.LEFT || alignment is PositionConstants.RIGHT) {
+        int orientation = getBlockFlow().getOrientation();
+        if (alignment is PositionConstants.LEFT)
+            alignment = orientation is DWT.LEFT_TO_RIGHT
+                    ? PositionConstants.ALWAYS_LEFT : PositionConstants.ALWAYS_RIGHT;
+        else
+            alignment = orientation is DWT.LEFT_TO_RIGHT
+                    ? PositionConstants.ALWAYS_RIGHT : PositionConstants.ALWAYS_LEFT;
+    }
+    if (alignment !is PositionConstants.CENTER && getBlockFlow().isMirrored())
+        alignment = (PositionConstants.ALWAYS_LEFT | PositionConstants.ALWAYS_RIGHT)
+                & ~alignment;
+
+    switch (alignment) {
+        case PositionConstants.ALWAYS_RIGHT:
+            line.setX(blockBox.getRecommendedWidth() - line.getWidth());
+            break;
+        case PositionConstants.CENTER:
+            line.setX((blockBox.getRecommendedWidth() - line.getWidth()) / 2);
+            break;
+        case PositionConstants.ALWAYS_LEFT:
+            line.setX(0);
+            break;
+        default:
+            throw new RuntimeException("Unexpected state"); //$NON-NLS-1$
+    }
+    blockBox.add(line);
+    previousLine = line;
+}
+
+/**
+ * Align the line horizontally and then commit it.
+ */
+protected void addCurrentLine() {
+    addBelowPreviousLine(currentLine);
+    (cast(LineRoot)currentLine).commit();
+}
+
+/**
+ * @see FlowContext#addLine(CompositeBox)
+ */
+public void addLine(CompositeBox box) {
+    endLine();
+    addBelowPreviousLine(box);
+}
+
+/**
+ * Marks the blocks contents as changed.  This means that children will be invalidated
+ * during validation.
+ * @since 3.1
+ */
+public void blockContentsChanged() {
+    blockInvalid = true;
+}
+
+/**
+ * @see FlowContainerLayout#cleanup()
+ */
+protected void cleanup() {
+    super.cleanup();
+    previousLine = null;
+}
+
+/**
+ * @see FlowContainerLayout#createNewLine()
+ */
+protected void createNewLine() {
+    currentLine = new LineRoot(getBlockFlow().isMirrored());
+    currentLine.setRecommendedWidth(blockBox.getRecommendedWidth());
+}
+
+/**
+ * Called by flush(), adds the BlockBox associated with this BlockFlowLayout
+ * to the current line and then ends the line.
+ */
+protected void endBlock() {
+    if (blockInvalid) {
+        Insets insets = getBlockFlow().getInsets();
+        blockBox.height += insets.getHeight();
+        blockBox.width += insets.getWidth();
+    }
+
+    if (getContext() !is null)
+        getContext().addLine(blockBox);
+
+    if (blockInvalid) {
+        blockInvalid = false;
+        List v = getFlowFigure().getChildren();
+        for (int i = 0; i < v.size(); i++)
+            (cast(FlowFigure)v.get(i)).postValidate();
+    }
+}
+
+/**
+ * @see FlowContext#endLine()
+ */
+public void endLine() {
+    if (currentLine is null || !currentLine.isOccupied())
+        return;
+    addCurrentLine();
+    currentLine = null;
+}
+
+/**
+ * @see FlowContainerLayout#flush()
+ */
+protected void flush() {
+    endLine();
+    endBlock();
+}
+
+bool forceChildInvalidation(Figure f) {
+    return blockInvalid;
+}
+
+/**
+ * Returns the BlockFlow associated with this BlockFlowLayout
+ * @return the BlockFlow
+ */
+protected final BlockFlow getBlockFlow() {
+    return cast(BlockFlow)getFlowFigure();
+}
+
+int getContextWidth() {
+    return getContext().getRemainingLineWidth();
+}
+
+/**
+ * @see FlowContext#getContinueOnSameLine()
+ */
+public bool getContinueOnSameLine() {
+    return continueOnSameLine;
+}
+
+/**
+ * @see FlowContext#getWidthLookahead(FlowFigure, int[])
+ */
+public void getWidthLookahead(FlowFigure child, int result[]) {
+    List children = getFlowFigure().getChildren();
+    int index = -1;
+    if (child !is null)
+        index = children.indexOf(child);
+
+    for (int i = index + 1; i < children.size(); i++)
+        if ((cast(FlowFigure)children.get(i)).addLeadingWordRequirements(result))
+            return;
+}
+
+/**
+ * @see FlowContainerLayout#preLayout()
+ */
+protected void preLayout() {
+    setContinueOnSameLine(false);
+    blockBox = getBlockFlow().getBlockBox_package();
+    setupBlock();
+    //Probably could setup current and previous line here, or just previous
+}
+
+/**
+ * @see dwtx.draw2d.text.FlowContext#setContinueOnSameLine(bool)
+ */
+public void setContinueOnSameLine(bool value) {
+    continueOnSameLine = value;
+}
+
+/**
+ * sets up the single block that contains all of the lines.
+ */
+protected void setupBlock() {
+    int recommended = getContextWidth();
+    if (recommended is Integer.MAX_VALUE)
+        recommended = -1;
+    BlockFlow bf = getBlockFlow();
+    if (recommended > 0) {
+        int borderCorrection = bf.getInsets().getWidth() + bf.getLeftMargin()
+                + bf.getRightMargin();
+        recommended = Math.max(0, recommended - borderCorrection);
+    }
+
+    if (recommended !is blockBox.recommendedWidth) {
+        blockInvalid = true;
+        blockBox.setRecommendedWidth(recommended);
+    }
+
+    if (blockInvalid) {
+        blockBox.height = 0;
+        blockBox.setWidth(Math.max(0, recommended));
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/CaretInfo.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.text.CaretInfo;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Translatable;
+
+/**
+ * Stores positional information about where a caret should be placed. This structure
+ * currently only offers integer precision. Scaling operations will result in rounding.
+ * @since 3.1
+ */
+public class CaretInfo
+    : Translatable
+{
+
+private int ascent, lineAscent, descent, lineDescent, baseline, x;
+
+/**
+ * Constructor for use by TextFlow. Constructs a new CaretInfo with the figure's ascent
+ * and descent and line information.
+ * <P>
+ * <EM>WARNING:</EM> This constructor should not be called by clients. It is for use by
+ * {@link TextFlow}, and may change in future releases.
+ * @param x the x location
+ * @param y the y location of the top of the caret
+ * @param ascent the ascent
+ * @param descent the descent
+ * @param lineAscent the ascent of the line on which the caret is placed
+ * @param lineDescent the descent of the line on which the caret is placed
+ */
+/+protected+/ this(int x, int y, int ascent, int descent, int lineAscent, int lineDescent) {
+    this.x = x;
+    this.baseline = y + ascent;
+    this.ascent = ascent;
+    this.descent = descent;
+    this.lineAscent = lineAscent;
+    this.lineDescent = lineDescent;
+}
+
+/**
+ * Constructs a CaretInfo object by copying the values from another instance.
+ * @param info the reference
+ * @since 3.2
+ */
+/+protected+/ this(CaretInfo info) {
+    this.ascent = info.ascent;
+    this.baseline = info.baseline;
+    this.descent = info.descent;
+    this.lineAscent = info.lineAscent;
+    this.lineDescent = info.lineDescent;
+    this.x = info.x;
+}
+
+/**
+ * Returns the y location of the baseline.
+ * @return the y coordinate of the baseline
+ */
+public int getBaseline() {
+    return baseline;
+}
+
+/**
+ * Returns the total height of the caret. The height is the sum of the ascent and descent.
+ * @return the height
+ */
+public int getHeight() {
+    return ascent + descent;
+}
+
+/**
+ * @return the total height of the line on which the caret is placed
+ */
+public int getLineHeight() {
+    return lineAscent + lineDescent;
+}
+
+/**
+ * @return the y location of the line on which the caret is placed
+ */
+public int getLineY() {
+    return baseline - lineAscent;
+}
+
+/**
+ * Returns the x location of the caret.
+ * @return the x coordinate
+ */
+public int getX() {
+    return x;
+}
+
+/**
+ * Returns the y location of the caret.
+ * @return the y coordinate
+ */
+public int getY() {
+    return baseline - ascent;
+}
+
+/**
+ * @see Translatable#performScale(double)
+ */
+public void performScale(double factor) {
+    x *= factor;
+    baseline *= factor;
+    descent *= factor;
+    ascent *= factor;
+    lineAscent *= factor;
+    lineDescent *= factor;
+}
+
+/**
+ * @see Translatable#performTranslate(int, int)
+ */
+public void performTranslate(int dx, int dy) {
+    x += dx;
+    baseline += dy;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/CompositeBox.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.CompositeBox;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.text.FlowBox;
+
+/**
+ * A FlowBox that can contain other FlowBoxes. The contained FlowBoxes are called
+ * <i>fragments</i>.
+ * @author hudsonr
+ * @since 2.1
+ */
+public abstract class CompositeBox
+    : FlowBox
+{
+
+int recommendedWidth = -1;
+
+/**
+ * Adds the given box and updates properties of this composite box.
+ * @param box the child being added
+ */
+public abstract void add(FlowBox box);
+
+
+abstract int getBottomMargin();
+
+/**
+ * Returns the recommended width for this CompositeBox.
+ * @return the recommended width
+ */
+public int getRecommendedWidth() {
+    return recommendedWidth;
+}
+
+abstract int getTopMargin();
+
+/**
+ * Sets the recommended width for this CompositeBox.
+ * @param w the width
+ */
+public void setRecommendedWidth(int w) {
+    recommendedWidth = w;
+}
+
+/**
+ * Positions the box vertically by setting the y coordinate for the top of the content of
+ * the line. For internal use only.
+ * @param top the y coordinate
+ * @since 3.1
+ */
+public abstract void setLineTop(int top);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/ContentBox.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.ContentBox;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.text.FlowBox;
+import dwtx.draw2d.text.LineRoot;
+
+/**
+ * FlowBoxes that are leaf nodes.
+ *
+ * @author Pratik Shah
+ * @since 3.1
+ */
+public abstract class ContentBox : FlowBox {
+
+private int bidiLevel = -1;
+private LineRoot lineRoot;
+
+/**
+ * @see FlowBox#getBaseline()
+ */
+public int getBaseline() {
+    return lineRoot.getBaseline();
+}
+
+/**
+ * @return the Bidi level of this box, if one has been set; -1 otherwise
+ * @see #setBidiLevel(int)
+ */
+public int getBidiLevel() {
+    return bidiLevel;
+}
+
+/**
+ * @see dwtx.draw2d.text.FlowBox#getLineRoot()
+ */
+LineRoot getLineRoot() {
+    return lineRoot;
+}
+
+/**
+ * Returns <code>true</code> if the bidi level for this box is specified, and is not the
+ * default level (0).
+ * @see dwtx.draw2d.text.FlowBox#requiresBidi()
+ */
+public bool requiresBidi() {
+    return bidiLevel > 0;
+}
+
+/**
+ * Sets the Bidi level of this fragment.  It is used to rearrange fragments as defined
+ * by the Unicode Bi-directional algorithm.  Valid values are -1 (meaning no Bidi level),
+ * or any non-negative integer less than 62.
+ * @param newLevel the new BidiLevel
+ * @see #getBidiLevel()
+ */
+public void setBidiLevel(int newLevel) {
+    bidiLevel = newLevel;
+}
+
+void setLineRoot(LineRoot root) {
+    this.lineRoot = root;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/FlowAdapter.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,171 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.FlowAdapter;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.text.FlowFigure;
+import dwtx.draw2d.text.BidiProcessor;
+import dwtx.draw2d.text.FlowFigureLayout;
+import dwtx.draw2d.text.FlowContext;
+import dwtx.draw2d.text.ContentBox;
+import dwtx.draw2d.text.BidiInfo;
+import dwtx.draw2d.text.BidiChars;
+
+
+/**
+ * Adapts non-flow figures for use within a parent hierarchy requiring flow figures.
+ * Normal draw2d figures can be added as children.  If a normal LayoutManager is set, the
+ * children will be positioned by that layout manager.  The size of this figure within
+ * the flow will be determined by its preferred size.
+ * <p>
+ * WARNING: This class is not intended to be subclassed by clients.
+ *
+ * @author Pratik Shah
+ * @since 3.1
+ */
+public class FlowAdapter
+    : FlowFigure
+{
+
+private FlowContext context;
+private FigureBox box;
+
+public this(){
+    box = new FigureBox();
+}
+/**
+ * This FlowFigure contributes an Object Replacement Character.
+ * @see FlowFigure#contributeBidi(BidiProcessor)
+ */
+protected void contributeBidi(BidiProcessor proc) {
+    box.setBidiLevel(-1);
+    // contributes a single object replacement char
+    proc.add(this, BidiChars.OBJ);
+}
+
+/**
+ * @return <code>null</code>
+ * @see dwtx.draw2d.text.FlowFigure#createDefaultFlowLayout()
+ */
+protected FlowFigureLayout createDefaultFlowLayout() {
+    return null;
+}
+
+/**
+ * Sizes the content box to be big enough to display all figures.  Wraps to the next line
+ * if there is not enough room on the current one.
+ * @see dwtx.draw2d.Figure#layout()
+ */
+protected void layout() {
+    int wHint = context.getRemainingLineWidth();
+    if (wHint is Integer.MAX_VALUE)
+        wHint = -1;
+    Dimension prefSize = getPreferredSize(wHint, -1);
+    if (context.isCurrentLineOccupied()
+            && prefSize.width > context.getRemainingLineWidth()) {
+        context.endLine();
+        prefSize = getPreferredSize(context.getRemainingLineWidth(), -1);
+    }
+    box.setSize(prefSize);
+    context.addToCurrentLine(box);
+}
+
+/**
+ * Updates the bounds of this figure to match that of its content box, and lays out this
+ * figure's children.
+ * @see FlowFigure#postValidate()
+ */
+public void postValidate() {
+    setBounds(new Rectangle(box.getX(), box.getBaseline() - box.ascent,
+            box.width, box.ascent));
+    super.layout();
+    for (Iterator itr = getChildren().iterator(); itr.hasNext();)
+        (cast(IFigure)itr.next()).validate();
+}
+
+/**
+ * Sets the bidi level of the content box associated with this Figure
+ * @see FlowFigure#setBidiInfo(BidiInfo)
+ */
+public void setBidiInfo(BidiInfo info) {
+    box.setBidiLevel(info.levelInfo[0]);
+}
+
+/**
+ * @see dwtx.draw2d.IFigure#setBounds(dwtx.draw2d.geometry.Rectangle)
+ */
+public void setBounds(Rectangle rect) {
+    int x = bounds.x,
+    y = bounds.y;
+
+    bool resize = (rect.width !is bounds.width) || (rect.height !is bounds.height),
+          translate = (rect.x !is x) || (rect.y !is y);
+
+    if ((resize || translate) && isVisible())
+        erase();
+    if (translate) {
+        int dx = rect.x - x;
+        int dy = rect.y - y;
+        primTranslate(dx, dy);
+    }
+
+    bounds.width = rect.width;
+    bounds.height = rect.height;
+
+    if (translate || resize) {
+        fireFigureMoved();
+        repaint();
+    }
+}
+
+/**
+ * @see FlowFigure#setFlowContext(FlowContext)
+ */
+public void setFlowContext(FlowContext flowContext) {
+    context = flowContext;
+}
+
+/**
+ * Do not validate children.
+ * @see dwtx.draw2d.IFigure#validate()
+ */
+public void validate() {
+    if (isValid())
+        return;
+    setValid(true);
+    layout();
+}
+
+private class FigureBox : ContentBox {
+    private int ascent;
+    public bool containsPoint(int x, int y) {
+        return this.outer.containsPoint(x, y);
+    }
+    public int getAscent() {
+        return ascent;
+    }
+    public int getDescent() {
+        return 0;
+    }
+    public void setSize(Dimension size) {
+        ascent = size.height;
+        width = size.width;
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/FlowBorder.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.text.FlowBorder;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.Border;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.text.FlowFigure;
+
+/**
+ * Experimental API. This is a special type of border for use with {@link
+ * dwtx.draw2d.text.FlowFigure}s. This interface should not be implemented by
+ * clients. Clients should extend {@link dwtx.draw2d.text.AbstractFlowBorder}.
+ * @since 3.1
+ */
+public interface FlowBorder : Border {
+
+/**
+ * Returns the collapsable bottom margin in pixels. Margin is the space external to the
+ * border and the flow box on which it is rendered. Vertical margins (top and bottom) may
+ * collapse in some situations, such as adjacent or nested blocks.
+ * @return the bottom margin
+ * @since 3.1
+ */
+int getBottomMargin();
+
+/**
+ * Returns the left margin in pixels. Margin is the space external to the border and the
+ * flow box on which it is rendered.
+ * @return the left margin
+ * @since 3.1
+ */
+int getLeftMargin();
+
+/**
+ * Returns the right margin in pixels. Margin is the space external to the border and the
+ * flow box on which it is rendered.
+ * @return the right margin
+ * @since 3.1
+ */
+int getRightMargin();
+
+/**
+ * Returns the collapsable top margin in pixels. Margin is the space external to the
+ * border and the flow box on which it is rendered. Vertical margins (top and bottom) may
+ * collapse in some situations, such as adjacent or nested blocks.
+ * @return the top margin
+ * @since 3.1
+ */
+int getTopMargin();
+
+/**
+ * Paints the border around the given box location. The border is asked to paint each of
+ * the FlowFigure's boxes. The <code>sideInfo</code> parameter is used to indicate whether
+ * the left and right sides should be rendered. This parameter will contain the following
+ * bit flags:
+ * <UL>
+ *   <LI>{@link dwt.DWT#LEAD}
+ *   <LI>{@link dwt.DWT#TRAIL}
+ *   <LI>{@link dwt.DWT#RIGHT_TO_LEFT}
+ * </UL>
+ * @param figure the flow figure whose border is being painted
+ * @param g the graphics
+ * @param where the relative location of the box
+ * @param sides bits indicating sides and bidi orientation
+ * @since 3.1
+ */
+void paint(FlowFigure figure, Graphics g, Rectangle where, int sides);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/FlowBox.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.FlowBox;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.text.LineRoot;
+
+/**
+ * A Geometric object for representing a region on a line of Text. This class adds the
+ * notion of a baseline to {@link dwtx.draw2d.geometry.Rectangle}. <i>Ascent</i> is
+ * the distance above the baseline. <i>Descent</i> is the distance below the baseline.
+ * <P>
+ * This class should not be treated as a <code>Rectangle</code> by clients.  It is
+ * important to use getters when available for lazy calculation of values.
+ *
+ * @author hudsonr
+ * @since 2.1
+ */
+public abstract class FlowBox {
+
+int width;
+
+/**
+ * The x location
+ */
+private int x;
+
+/**
+ * This method must be called on a block that is completely positioned and committed.
+ * @param x X
+ * @param y Y
+ * @return <code>true</code> if the FlowBox contains the point
+ */
+public abstract bool containsPoint(int x, int y);
+
+/**
+ * Returns the amount of line content in pixels which is above the baseline.  Ascent and
+ * descent are used to space consecutive lines apart. Certain types of line content, such
+ * as borders, extend beyond the ascent and descent.
+ * @return the <i>descent</i> in pixels below the baseline
+ */
+public abstract int getAscent();
+
+/**
+ * Returns y coordinate for the box's baseline.
+ * @return the baseline location
+ * @since 3.1
+ */
+public abstract int getBaseline();
+
+/**
+ * Returns the amount of line content in pixels which is below the baseline.
+ * @return the <i>descent</i> in pixels
+ * @see #getAscent()
+ */
+public abstract int getDescent();
+
+/**
+ * Returns the root LineBox in which this box is placed.  The root line is interesting
+ * when painting selection or hit testing.  All boxes in a line should render selection at
+ * the same top and bottom location.
+ * @return the line root.
+ * @since 3.1
+ */
+abstract LineRoot getLineRoot();
+
+/**
+ * Returns the outer ascent of this box.  The outer ascent is the ascent above the
+ * baseline including the border size and margin.  This is used when adding content into a
+ * LineBox.  The linebox's own border must be drawn around the children.
+ */
+int getOuterAscent() {
+    return getAscent();
+}
+
+/**
+ * Returns the outer descent of this box. The outer descent is the space below the
+ * baseline including the border size and margin. This is used when adding content into a
+ * LineBox. The linebox's own border must be drawn around the children.
+ */
+int getOuterDescent() {
+    return getDescent();
+}
+
+int getAscentWithBorder() {
+    throw new RuntimeException("Not valid on this box type"); //$NON-NLS-1$
+}
+
+int getDescentWithBorder() {
+    throw new RuntimeException("Not valid on this box type"); //$NON-NLS-1$
+}
+
+/**
+ * Returns the width of the box.
+ * @return the box's width
+ */
+public int getWidth() {
+    return width;
+}
+
+/**
+ * Returns the X coordinate of the box.
+ * @return the x coordinate
+ * @since 3.1
+ */
+public int getX() {
+    return x;
+}
+
+/**
+ * Returns <code>true</code> if any of the children are bi-directional.  Default
+ * implementation returns false.
+ *
+ * @return <code>true</code> if the box is bi-directional
+ * @since 3.1
+ */
+public bool requiresBidi() {
+    return false;
+}
+
+/**
+ * Sets the line root.
+ * @param root the line root
+ * @since 3.1
+ */
+void setLineRoot(LineRoot root) {
+}
+
+/**
+ * Sets the width of the box.
+ * @param width the new width
+ * @since 3.1
+ */
+public void setWidth(int width) {
+    this.width = width;
+}
+
+/**
+ * Sets the x coordinate for this box.
+ * @param x the x coordinate
+ * @since 3.1
+ */
+public void setX(int x) {
+    this.x = x;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/FlowContainerLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.FlowContainerLayout;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.Figure;
+import dwtx.draw2d.text.FlowFigureLayout;
+import dwtx.draw2d.text.FlowBox;
+import dwtx.draw2d.text.LineBox;
+import dwtx.draw2d.text.FlowContext;
+import dwtx.draw2d.text.FlowFigure;
+
+/**
+ * A layout for FlowFigures with children.
+ *
+ * <P>WARNING: This class is not intended to be subclassed by clients.
+ * @author hudsonr
+ * @since 2.1
+ */
+public abstract class FlowContainerLayout
+    : FlowFigureLayout
+    , FlowContext
+{
+
+/**
+ * the current line
+ */
+LineBox currentLine;
+
+/**
+ * @see dwtx.draw2d.text.FlowFigureLayout#FlowFigureLayout(FlowFigure)
+ */
+protected this(FlowFigure flowFigure) {
+    super(flowFigure);
+}
+
+/**
+ * Adds the given box the current line and clears the context's state.
+ * @see dwtx.draw2d.text.FlowContext#addToCurrentLine(FlowBox)
+ */
+public void addToCurrentLine(FlowBox child) {
+    getCurrentLine().add(child);
+    setContinueOnSameLine(false);
+}
+
+/**
+ * Flush anything pending and free all temporary data used during layout.
+ */
+protected void cleanup() {
+    currentLine = null;
+}
+
+/**
+ * Used by getCurrentLine().
+ */
+protected abstract void createNewLine();
+
+/**
+ * Called after {@link #layoutChildren()} when all children have been laid out. This
+ * method exists to flush the last line.
+ */
+protected abstract void flush();
+
+/**
+ * FlowBoxes shouldn't be added directly to the current line.  Use
+ * {@link #addToCurrentLine(FlowBox)} for that.
+ * @see dwtx.draw2d.text.FlowContext#getCurrentLine()
+ */
+LineBox getCurrentLine() {
+    if (currentLine is null)
+        createNewLine();
+    return currentLine;
+}
+
+/**
+ * @see FlowContext#getRemainingLineWidth()
+ */
+public int getRemainingLineWidth() {
+    return getCurrentLine().getAvailableWidth();
+}
+
+/**
+ * @see FlowContext#isCurrentLineOccupied()
+ */
+public bool isCurrentLineOccupied() {
+    return currentLine !is null && currentLine.isOccupied();
+}
+
+/**
+ * @see FlowFigureLayout#layout()
+ */
+protected void layout() {
+    preLayout();
+    layoutChildren();
+    flush();
+    cleanup();
+}
+
+/**
+ * Layout all children.
+ */
+protected void layoutChildren() {
+    List children = getFlowFigure().getChildren();
+    for (int i = 0; i < children.size(); i++) {
+        Figure f = cast(Figure)children.get(i);
+        if (forceChildInvalidation(f))
+            f.invalidate();
+        f.validate();
+    }
+}
+
+bool forceChildInvalidation(Figure f) {
+    return true;
+}
+
+/**
+ * Called before layoutChildren() to setup any necessary state.
+ */
+protected abstract void preLayout();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/FlowContext.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.FlowContext;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.text.FlowBox;
+import dwtx.draw2d.text.CompositeBox;
+import dwtx.draw2d.text.FlowFigure;
+
+/**
+ * The context that a {@link FlowFigureLayout} uses to perform its layout.
+ *
+ * <P>WARNING: This interface is not intended to be implemented by clients. It exists to
+ * define the API between the layout and its context.
+ */
+public interface FlowContext {
+
+/**
+ * Adds the given box into the current line.
+ * @param box the FlowBox to add
+ */
+void addToCurrentLine(FlowBox box);
+
+/**
+ * Adds an entire line into the context. If there is a previous line, it is ended.
+ * @param box the line being added
+ * @since 3.1
+ */
+void addLine(CompositeBox box);
+
+/**
+ * The current line should be committed if it is occupied, and then set to
+ * <code>null</code>. Otherwise, do nothing.
+ */
+void endLine();
+
+/**
+ * This method can be used to query the amount of space left on the current line.  It
+ * can help determine where to wrap during layout.
+ * @return the amount of space left on the current line
+ * @since 3.1
+ */
+int getRemainingLineWidth();
+
+/**
+ * This method is used to convey layout state to different FlowFigures.  This state is
+ * cleared when a fragment is added to the current line and once the layout is complete.
+ * @return <code>true</code> if the next fragment should be placed on the current line
+ * @since 3.1
+ * @see #setContinueOnSameLine(bool)
+ */
+bool getContinueOnSameLine();
+
+/**
+ * This method looks ahead for line-breaks.  When laying out, this method can be used
+ * to determine the next line-break across multiple figures.
+ *
+ * @param child the search will occur starting from the figure after the given child
+ * @param width the width before the next line-break (if one's found; all the width,
+ * otherwise) will be added on to the first int in the given array
+ * @since 3.1
+ */
+void getWidthLookahead(FlowFigure child, int width[]);
+
+/**
+ * @return <code>true</code> if the current line contains any fragments
+ */
+bool isCurrentLineOccupied();
+
+/**
+ * This method is used to convey layout state to different FlowFigures.  This state is
+ * cleared when a fragment is added and once the layout is complete.
+ *
+ * @param value <code>true</code> indicates that the first fragment of the next TextFlow
+ * should be laid out on the current line, and not a new one
+ * @since 3.1
+ * @see #getContinueOnSameLine()
+ */
+void setContinueOnSameLine(bool value);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/FlowFigure.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,193 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.FlowFigure;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.Figure;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.text.BidiProcessor;
+import dwtx.draw2d.text.FlowFigureLayout;
+import dwtx.draw2d.text.BidiInfo;
+import dwtx.draw2d.text.FlowContext;
+
+/**
+ * The base implementation for text flow figures. A flow figure is used to render a
+ * document in which elements are laid out horizontally within a "line" until that line is
+ * filled. Layout continues on the next line.
+ *
+ * <p>WARNING: This class is not intended to be subclassed by clients. Future versions may
+ * contain additional abstract methods.
+ *
+ * @author hudsonr
+ * @since 2.1
+ */
+public abstract class FlowFigure
+    : Figure
+{
+    alias Figure.add add;
+
+/**
+ * integer indicating whether selection should be displayed.
+ */
+protected int selectionStart = -1;
+
+/**
+ * Constructs a new FlowFigure.
+ */
+public this() {
+    setLayoutManager(createDefaultFlowLayout());
+}
+
+/**
+ * If the child is a <code>FlowFigure</code>, its FlowContext is passed to it.
+ * @see dwtx.draw2d.IFigure#add(IFigure, Object, int)
+ */
+public void add(IFigure child, Object constraint, int index) {
+    super.add(child, constraint, index);
+    //If this layout manager is a FlowContext, then the child *must* be a FlowFigure
+    if (null !is cast(FlowContext)getLayoutManager() )
+        (cast(FlowFigure)child).setFlowContext(cast(FlowContext)getLayoutManager());
+    revalidateBidi(this);
+}
+
+/**
+ * Calculates the width of text before the next line-break is encountered.
+ * <p>
+ * Default implementation treats each FlowFigure as a line-break.  It adds no width and
+ * returns <code>true</code>.  Sub-classes should override as needed.
+ *
+ * @param width the width before the next line-break (if one's found; all the width,
+ * otherwise) will be added on to the first int in the given array
+ * @return bool indicating whether or not a line-break was found
+ * @since 3.1
+ */
+public bool addLeadingWordRequirements(int[] width) {
+    return true;
+}
+
+/**
+ * FlowFigures can contribute text for their block to the given {@link BidiProcessor},
+ * which will process the contributions to determine Bidi levels and shaping requirements.
+ * <p>
+ * This method is invoked as part of validating Bidi.
+ * <p>
+ * Sub-classes that cache the BidiInfo and/or the bidi level in ContentBoxes should clear
+ * the cached values when this method is invoked.
+ *
+ * @param proc the BidiProcessor to which contributions should be made
+ * @see BidiProcessor#add(FlowFigure, String)
+ * @since 3.1
+ */
+protected void contributeBidi(BidiProcessor proc) {
+    for (Iterator iter = getChildren().iterator(); iter.hasNext();)
+        (cast(FlowFigure)iter.next()).contributeBidi(proc);
+}
+
+/**
+ * Creates the default layout manager
+ * @return The default layout
+ */
+protected abstract FlowFigureLayout createDefaultFlowLayout();
+
+/**
+ * Called after validate has occurred. This is used to update the bounds of the FlowFigure
+ * to encompass its new flow boxed created during validate.
+ */
+public abstract void postValidate();
+
+/**
+ * Overridden to revalidateBidi when fragments are removed.
+ * @see dwtx.draw2d.IFigure#remove(dwtx.draw2d.IFigure)
+ */
+public void remove(IFigure figure) {
+    super.remove(figure);
+    revalidateBidi(this);
+}
+
+/**
+ * This method should be invoked whenever a change that can potentially affect the
+ * Bidi evaluation is made (eg., adding or removing children, changing text, etc.).
+ * <p>
+ * The default implementation delegates the revalidation task to the parent.  Only
+ * {@link BlockFlow#revalidateBidi(IFigure) blocks} perform the actual revalidation.
+ * <p>
+ * The given IFigure is the one that triggered the revalidation.  This can be used to
+ * optimize bidi evaluation.
+ *
+ * @param origin the figure that was revalidated
+ * @since 3.1
+ */
+protected void revalidateBidi(IFigure origin) {
+    if (getParent() !is null)
+        (cast(FlowFigure)getParent()).revalidateBidi(origin);
+}
+
+/**
+ * Sets the bidi information for this figure.  A flow figure contributes bidi text in
+ * {@link #contributeBidi(BidiProcessor)}.  If the figure contributes text associated with
+ * it, this method is called back to indicate the bidi properties for that text within its
+ * block.
+ *
+ * @param info the BidiInfo for this figure
+ * @since 3.1
+ */
+public void setBidiInfo(BidiInfo info) { }
+
+/**
+ * FlowFigures override setBounds() to prevent translation of children. "bounds" is a
+ * derived property for FlowFigures, calculated from the fragments that make up the
+ * FlowFigure.
+ * @see Figure#setBounds(Rectangle)
+ */
+public void setBounds(Rectangle r) {
+    if (bounds.opEquals(r))
+        return;
+    if (!r.contains(bounds))
+        erase();
+    bounds.x = r.x;
+    bounds.y = r.y;
+    bounds.width = r.width;
+    bounds.height = r.height;
+    fireFigureMoved();
+    if (isCoordinateSystem())
+        fireCoordinateSystemChanged();
+    repaint();
+}
+
+/**
+ * Sets the flow context.
+ * @param flowContext the flow context for this flow figure
+ */
+public void setFlowContext(FlowContext flowContext) {
+    (cast(FlowFigureLayout)getLayoutManager()).setFlowContext(flowContext);
+}
+
+/**
+ * Sets the selection or a range of selection.  A start value of -1 is used to indicate no
+ * selection.  A start value >=0 indicates show selection.  A start and end value can be
+ * used to represent a range of offsets which should render selection.
+ * @param start the start offset
+ * @param end the end offset
+ * @since 3.1
+ */
+public void setSelection(int start, int end) {
+    if (selectionStart is start)
+        return;
+    selectionStart = start;
+    repaint();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/FlowFigureLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.FlowFigureLayout;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.LayoutManager;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.text.FlowContext;
+import dwtx.draw2d.text.FlowFigure;
+
+/**
+ * A LayoutManager for use with FlowFigure.
+ *
+ * <P>WARNING: This class is not intended to be subclassed by clients.
+ * @author hudsonr
+ * @since 2.1
+ */
+public abstract class FlowFigureLayout
+    : LayoutManager
+{
+
+/**
+ * The flow context in which this LayoutManager exists.
+ */
+private FlowContext context;
+
+/**
+ * The figure passed by layout(Figure) is held for convenience.
+ */
+private final FlowFigure flowFigure;
+
+/**
+ * Constructs a new FlowFigureLayout with the given FlowFigure.
+ * @param flowfigure the FlowFigure
+ */
+protected this(FlowFigure flowfigure) {
+    this.flowFigure = flowfigure;
+}
+
+/**
+ * Not applicable.
+ * @see dwtx.draw2d.LayoutManager#getConstraint(dwtx.draw2d.IFigure)
+ */
+public Object getConstraint(IFigure child) {
+    return null;
+}
+
+/**
+ * Returns this layout's context or <code>null</code>.
+ * @return <code>null</code> or a context
+ * @since 3.1
+ */
+protected FlowContext getContext() {
+    return context;
+}
+
+/**
+ * @return the FlowFigure
+ */
+protected FlowFigure getFlowFigure() {
+    return flowFigure;
+}
+
+/**
+ * Not applicable.
+ * @see dwtx.draw2d.LayoutManager#getMinimumSize(dwtx.draw2d.IFigure, int, int)
+ */
+public Dimension getMinimumSize(IFigure container, int wHint, int hHint) {
+    return null;
+}
+
+/**
+ * Not applicable.
+ * @see dwtx.draw2d.LayoutManager#getPreferredSize(dwtx.draw2d.IFigure, int, int)
+ */
+public Dimension getPreferredSize(IFigure container, int wHint, int hHint) {
+    return null;
+}
+
+/**
+ * Not applicable.
+ * @see dwtx.draw2d.LayoutManager#invalidate()
+ */
+public void invalidate() { }
+
+/**
+ * Called during {@link #layout(IFigure)}.
+ */
+protected abstract void layout();
+
+/**
+ * @see dwtx.draw2d.LayoutManager#layout(IFigure)
+ */
+public final void layout(IFigure figure) {
+    layout ();
+}
+
+/**
+ * Not applicable.
+ * @see dwtx.draw2d.LayoutManager#remove(dwtx.draw2d.IFigure)
+ */
+public void remove(IFigure child) { }
+
+/**
+ * Not applicable.
+ * @see dwtx.draw2d.LayoutManager#setConstraint(dwtx.draw2d.IFigure, java.lang.Object)
+ */
+public void setConstraint(IFigure child, Object constraint) { }
+
+/**
+ * Sets the context for this layout manager.
+ * @param flowContext the context of this layout
+ */
+public void setFlowContext(FlowContext flowContext) {
+    context = flowContext;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/FlowPage.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.FlowPage;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.text.BlockFlow;
+import dwtx.draw2d.text.FlowFigureLayout;
+import dwtx.draw2d.text.PageFlowLayout;
+import dwtx.draw2d.text.FlowFigure;
+
+/**
+ * The root of a Flow hierarchy. A flow page can be treated as a normal figure, but
+ * contains FlowFigures.
+ * <P>
+ * A FlowPage will not have a defined width unless it is inside a figure whose layout
+ * provides width hints when calling
+ * {@link dwtx.draw2d.IFigure#getPreferredSize(int, int)}.
+ *
+ * <P>WARNING: This class is not intended to be subclassed by clients.
+ */
+public class FlowPage
+    : BlockFlow
+{
+    alias BlockFlow.add add;
+
+private Dimension pageSize;
+private int recommendedWidth;
+private int[] pageSizeCacheKeys;
+private Dimension[] pageSizeCacheValues;
+
+public this(){
+    pageSize = new Dimension();
+    pageSizeCacheKeys = new int[3];
+    pageSizeCacheValues = new Dimension[3];
+}
+/**
+ * @see dwtx.draw2d.Figure#addNotify()
+ */
+public void addNotify() {
+    super.addNotify();
+    setValid(false);
+}
+
+/**
+ * @see dwtx.draw2d.text.BlockFlow#createDefaultFlowLayout()
+ */
+protected FlowFigureLayout createDefaultFlowLayout() {
+    return new PageFlowLayout(this);
+}
+
+/**
+ * @see dwtx.draw2d.Figure#getMinimumSize(int, int)
+ */
+public Dimension getMinimumSize(int w, int h) {
+    return getPreferredSize(w, h);
+}
+
+/**
+ * @see dwtx.draw2d.Figure#invalidate()
+ */
+public void invalidate() {
+    pageSizeCacheValues = new Dimension[3];
+    super.invalidate();
+}
+
+/**
+ * @see dwtx.draw2d.Figure#getPreferredSize(int, int)
+ */
+public Dimension getPreferredSize(int width, int h) {
+    for (int i = 0; i < 3; i++) {
+        if (pageSizeCacheKeys[i] is width && pageSizeCacheValues[i] !is null)
+            return pageSizeCacheValues[i];
+    }
+
+    pageSizeCacheKeys[2] = pageSizeCacheKeys[1];
+    pageSizeCacheKeys[1] = pageSizeCacheKeys[0];
+    pageSizeCacheKeys[0] = width;
+
+    pageSizeCacheValues[2] = pageSizeCacheValues[1];
+    pageSizeCacheValues[1] = pageSizeCacheValues[0];
+
+    //Flowpage must temporarily layout to determine its preferred size
+    int oldWidth = getPageWidth();
+    setPageWidth(width);
+    validate();
+    pageSizeCacheValues[0] = pageSize.getCopy();
+
+    if (width !is oldWidth) {
+        setPageWidth(oldWidth);
+        getUpdateManager().addInvalidFigure(this);
+    }
+    return pageSizeCacheValues[0];
+}
+
+int getPageWidth() {
+    return recommendedWidth;
+}
+
+/**
+ * @see BlockFlow#postValidate()
+ */
+public void postValidate() {
+    Rectangle r = getBlockBox().toRectangle();
+    pageSize.width = r.width;
+    pageSize.height = r.height;
+    List v = getChildren();
+    for (int i = 0; i < v.size(); i++)
+        (cast(FlowFigure)v.get(i)).postValidate();
+}
+
+/**
+ * Overridden to set valid.
+ * @see dwtx.draw2d.IFigure#removeNotify()
+ */
+public void removeNotify() {
+    super.removeNotify();
+    setValid(true);
+}
+
+/**
+ * @see FlowFigure#setBounds(Rectangle)
+ */
+public void setBounds(Rectangle r) {
+    if (getBounds().opEquals(r))
+        return;
+    bool invalidate = getBounds().width !is r.width || getBounds().height !is r.height;
+    super.setBounds(r);
+    int newWidth = r.width;
+    if (invalidate || getPageWidth() !is newWidth) {
+        setPageWidth(newWidth);
+        getUpdateManager().addInvalidFigure(this);
+    }
+}
+
+private void setPageWidth(int width) {
+    if (recommendedWidth is width)
+        return;
+    recommendedWidth = width;
+    super.invalidate();
+}
+
+/**
+ * @see dwtx.draw2d.Figure#validate()
+ */
+public void validate() {
+    if (isValid())
+        return;
+    super.validate();
+    postValidate();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/FlowUtilities.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,409 @@
+/*******************************************************************************
+ * 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.FlowUtilities;
+
+import dwt.dwthelper.utils;
+
+import dwtx.dwtxhelper.mangoicu.UBreakIterator;
+import dwtx.dwtxhelper.mangoicu.ULocale;
+
+import dwt.DWT;
+import dwt.graphics.Font;
+import dwt.graphics.Rectangle;
+import dwt.graphics.TextLayout;
+import dwt.widgets.Display;
+import dwtx.draw2d.FigureUtilities;
+import dwtx.draw2d.TextUtilities;
+import dwtx.draw2d.text.TextFragmentBox;
+import dwtx.draw2d.text.InlineFlow;
+import dwtx.draw2d.text.FlowContext;
+import dwtx.draw2d.text.FlowBorder;
+import dwtx.draw2d.text.ParagraphTextLayout;
+import dwtx.draw2d.text.TextFlow;
+
+interface LookAhead {
+    int getWidth();
+}
+
+/**
+ * Utility class for FlowFigures.
+ * @author hudsonr
+ * @since 3.4
+ */
+public class FlowUtilities
+{
+
+/**
+ * a singleton default instance
+ */
+public static FlowUtilities INSTANCE;
+
+private static const UBreakIterator INTERNAL_LINE_BREAK;
+private static TextLayout layout;
+
+static const UBreakIterator LINE_BREAK;
+
+static this(){
+    INSTANCE = new FlowUtilities();
+    INTERNAL_LINE_BREAK = UBreakIterator.openLineIterator( ULocale.Default );
+    LINE_BREAK = UBreakIterator.openLineIterator( ULocale.Default );
+}
+
+static bool canBreakAfter(dchar c) {
+    bool result = CharacterIsWhitespace(c) || c is '-';
+    if (!result && (c < 'a' || c > 'z')) {
+        // chinese characters and such would be caught in here
+        // LINE_BREAK is used here because INTERNAL_LINE_BREAK might be in use
+        LINE_BREAK.setText(dcharToString(c) ~ "a"); //$NON-NLS-1$
+        result = LINE_BREAK.isBoundary(1);
+    }
+    return result;
+}
+
+private static int findFirstDelimeter(String string) {
+    int macNL = string.indexOf('\r');
+    int unixNL = string.indexOf('\n');
+
+    if (macNL is -1)
+        macNL = Integer.MAX_VALUE;
+    if (unixNL is -1)
+        unixNL = Integer.MAX_VALUE;
+
+    return Math.min(macNL, unixNL);
+}
+
+/**
+ * Gets the average character width.
+ *
+ * @param fragment the supplied TextFragmentBox to use for calculation.
+ *                 if the length is 0 or if the width is or below 0,
+ *                 the average character width is taken from standard
+ *                 font metrics.
+ * @param font     the font to use in case the TextFragmentBox conditions
+ *                 above are true.
+ * @return         the average character width
+ */
+protected float getAverageCharWidth(TextFragmentBox fragment, Font font) {
+    if (fragment.getWidth() > 0 && fragment.length !is 0)
+        return fragment.getWidth() / cast(float)fragment.length;
+    return FigureUtilities.getFontMetrics(font).getAverageCharWidth();
+}
+
+static int getBorderAscent(InlineFlow owner) {
+    if (null !is cast(FlowBorder)owner.getBorder() ) {
+        FlowBorder border = cast(FlowBorder)owner.getBorder();
+        return border.getInsets(owner).top;
+    }
+    return 0;
+}
+
+static int getBorderAscentWithMargin(InlineFlow owner) {
+    if (null !is cast(FlowBorder)owner.getBorder() ) {
+        FlowBorder border = cast(FlowBorder)owner.getBorder();
+        return border.getTopMargin() + border.getInsets(owner).top;
+    }
+    return 0;
+}
+
+static int getBorderDescent(InlineFlow owner) {
+    if (null !is cast(FlowBorder)owner.getBorder() ) {
+        FlowBorder border = cast(FlowBorder)owner.getBorder();
+        return border.getInsets(owner).bottom;
+    }
+    return 0;
+}
+
+static int getBorderDescentWithMargin(InlineFlow owner) {
+    if (null !is cast(FlowBorder)owner.getBorder() ) {
+        FlowBorder border = cast(FlowBorder)owner.getBorder();
+        return border.getBottomMargin() + border.getInsets(owner).bottom;
+    }
+    return 0;
+}
+
+/**
+ * Provides a TextLayout that can be used by the Draw2d text package for Bidi.  This
+ * TextLayout should not be disposed by clients.  The provided TextLayout's orientation
+ * will be LTR.
+ *
+ * @return an DWT TextLayout that can be used for Bidi
+ * @since 3.1
+ */
+static TextLayout getTextLayout() {
+    if (layout is null)
+        layout = new TextLayout(Display.getDefault());
+    layout.setOrientation(DWT.LEFT_TO_RIGHT);
+    return layout;
+}
+
+/**
+ * @param frag
+ * @param string
+ * @param font
+ * @since 3.1
+ */
+private static void initBidi(TextFragmentBox frag, String string, Font font) {
+    if (frag.requiresBidi()) {
+        TextLayout textLayout = getTextLayout();
+        textLayout.setFont(font);
+        //$TODO need to insert overrides in front of string.
+        textLayout.setText(string);
+    }
+}
+
+private int measureString(TextFragmentBox frag, String string, int guess, Font font) {
+    if (frag.requiresBidi()) {
+        // The text and/or could have changed if the lookAhead was invoked.  This will
+        // happen at most once.
+        return getTextLayoutBounds(string, font, 0, guess - 1).width;
+    } else
+        return getTextUtilities().getStringExtents(string.substring(0, guess), font).width;
+}
+
+/**
+ * Sets up the fragment width based using the font and string passed in.
+ *
+ * @param fragment
+ *            the text fragment whose width will be set
+ * @param font
+ *            the font to be used in the calculation
+ * @param string
+ *            the string to be used in the calculation
+ */
+final protected void setupFragment(TextFragmentBox fragment, Font font, String string) {
+    if (fragment.getWidth() is -1 || fragment.isTruncated()) {
+        int width;
+        if (string.length is 0 || fragment.length is 0)
+            width = 0;
+        else if (fragment.requiresBidi()) {
+            width = getTextLayoutBounds(string, font, 0, fragment.length - 1).width;
+        } else
+            width = getTextUtilities().getStringExtents(string.substring(0, fragment.length), font).width;
+        if (fragment.isTruncated())
+            width += getEllipsisWidth(font);
+        fragment.setWidth(width);
+    }
+}
+package void setupFragment_package(TextFragmentBox fragment, Font font, String string) {
+    setupFragment( fragment, font, string );
+}
+
+/**
+ * Sets up a fragment and returns the number of characters consumed from the given
+ * String. An average character width can be provided as a hint for faster calculation.
+ * If a fragment's bidi level is set, a TextLayout will be used to calculate the width.
+ *
+ * @param frag the TextFragmentBox
+ * @param string the String
+ * @param font the Font used for measuring
+ * @param context the flow context
+ * @param wrapping the word wrap style
+ * @return the number of characters that will fit in the given space; can be 0 (eg., when
+ * the first character of the given string is a newline)
+ */
+final protected int wrapFragmentInContext(TextFragmentBox frag, String string,
+        FlowContext context, LookAhead lookahead, Font font, int wrapping) {
+    frag.setTruncated(false);
+    int strLen = string.length;
+    if (strLen is 0) {
+        frag.setWidth(-1);
+        frag.length = 0;
+        setupFragment(frag, font, string);
+        context.addToCurrentLine(frag);
+        return 0;
+    }
+
+    INTERNAL_LINE_BREAK.setText(string);
+
+    initBidi(frag, string, font);
+    float avgCharWidth = getAverageCharWidth(frag, font);
+    frag.setWidth(-1);
+
+    /*
+     * Setup initial boundaries within the string.
+     */
+    int absoluteMin = 0;
+    int max, min = 1;
+    if (wrapping is ParagraphTextLayout.WORD_WRAP_HARD) {
+        absoluteMin = INTERNAL_LINE_BREAK.next();
+        while (absoluteMin > 0 && CharacterIsWhitespace(string[absoluteMin - 1 .. $].firstCodePoint()))
+            absoluteMin--;
+        min = Math.max(absoluteMin, 1);
+    }
+    int firstDelimiter = findFirstDelimeter(string);
+    if (firstDelimiter is 0)
+        min = max = 0;
+    else
+        max = Math.min(strLen, firstDelimiter) + 1;
+
+
+    int availableWidth = context.getRemainingLineWidth();
+    int guess = 0, guessSize = 0;
+
+    while (true) {
+        if ((max - min) <= 1) {
+            if (min is absoluteMin
+                    && context.isCurrentLineOccupied()
+                    && !context.getContinueOnSameLine()
+                    && availableWidth < measureString(frag, string, min, font)
+                        + ((min is strLen && lookahead !is null) ? lookahead.getWidth() : 0)
+            ) {
+                context.endLine();
+                availableWidth = context.getRemainingLineWidth();
+                max = Math.min(strLen, firstDelimiter) + 1;
+                if ((max - min) <= 1)
+                    break;
+            } else
+                break;
+        }
+        // Pick a new guess size
+        // New guess is the last guess plus the missing width in pixels
+        // divided by the average character size in pixels
+        guess += 0.5f + (availableWidth - guessSize) / avgCharWidth;
+
+        if (guess >= max) guess = max - 1;
+        if (guess <= min) guess = min + 1;
+
+        guessSize = measureString(frag, string, guess, font);
+
+        if (guess is strLen
+                && lookahead !is null
+                && !canBreakAfter(string.charAt(strLen - 1))
+                && guessSize + lookahead.getWidth() > availableWidth) {
+            max = guess;
+            continue;
+        }
+
+        if (guessSize <= availableWidth) {
+            min = guess;
+            frag.setWidth(guessSize);
+            if (guessSize is availableWidth)
+                max = guess + 1;
+        } else
+            max = guess;
+    }
+
+    int result = min;
+    bool continueOnLine = false;
+    if (min is strLen) {
+        //Everything fits
+        if (string.charAt(strLen - 1) is ' ') {
+            if (frag.getWidth() is -1) {
+                frag.length = result;
+                frag.setWidth(measureString(frag, string, result, font));
+            }
+            if (lookahead.getWidth() > availableWidth - frag.getWidth()) {
+                frag.length = result - 1;
+                frag.setWidth(-1);
+            } else
+                frag.length = result;
+        } else {
+            continueOnLine = !canBreakAfter(string.charAt(strLen - 1));
+            frag.length = result;
+        }
+    } else if (min is firstDelimiter) {
+        //move result past the delimiter
+        frag.length = result;
+        if (string.charAt(min) is '\r') {
+            result++;
+            if (++min < strLen && string.charAt(min) is '\n')
+                result++;
+        } else if (string.charAt(min) is '\n')
+            result++;
+    } else if (string.charAt(min) is ' '
+            || canBreakAfter(string.charAt(min - 1))
+            || INTERNAL_LINE_BREAK.isBoundary(min)) {
+        frag.length = min;
+        if (string.charAt(min) is ' ')
+            result++;
+        else if (string.charAt(min - 1) is ' ') {
+            frag.length--;
+            frag.setWidth(-1);
+        }
+    } else {
+out_:
+        // In the middle of an unbreakable offset
+        result = INTERNAL_LINE_BREAK.previous(min);
+        if (result is 0) {
+            switch (wrapping) {
+                case ParagraphTextLayout.WORD_WRAP_TRUNCATE :
+                    int truncatedWidth = availableWidth - getEllipsisWidth(font);
+                    if (truncatedWidth > 0) {
+                        //$TODO this is very slow.  It should be using avgCharWidth to go faster
+                        while (min > 0) {
+                            guessSize = measureString(frag, string, min, font);
+                            if (guessSize <= truncatedWidth)
+                                break;
+                            min--;
+                        }
+                        frag.length = min;
+                    } else
+                        frag.length = 0;
+                    frag.setTruncated(true);
+                    result = INTERNAL_LINE_BREAK.next(max - 1);
+                    goto out_;
+
+                default:
+                    result = min;
+                    break;
+            }
+        }
+        frag.length = result;
+        if (string.charAt(result - 1) is ' ')
+            frag.length--;
+        frag.setWidth(-1);
+    }
+
+    setupFragment(frag, font, string);
+    context.addToCurrentLine(frag);
+    context.setContinueOnSameLine(continueOnLine);
+    return result;
+}
+package int wrapFragmentInContext_package(TextFragmentBox frag, String string,
+        FlowContext context, LookAhead lookahead, Font font, int wrapping) {
+    return wrapFragmentInContext( frag, string, context, lookahead, font, wrapping );
+}
+
+/**
+ * @see TextLayout#getBounds()
+ */
+protected Rectangle getTextLayoutBounds(String s, Font f, int start, int end) {
+    TextLayout textLayout = getTextLayout();
+    textLayout.setFont(f);
+    textLayout.setText(s);
+    return textLayout.getBounds(start, end);
+}
+
+/**
+ * Returns an instance of a <code>TextUtililities</code> class on which
+ * text calculations can be performed. Clients may override to customize.
+ *
+ * @return the <code>TextUtililities</code> instance
+ * @since 3.4
+ */
+protected TextUtilities getTextUtilities() {
+    return TextUtilities.INSTANCE;
+}
+
+/**
+ * Gets the ellipsis width.
+ *
+ * @param font
+ *            the font to be used in the calculation
+ * @return the width of the ellipsis
+ * @since 3.4
+ */
+private int getEllipsisWidth(Font font) {
+    return getTextUtilities().getStringExtents(TextFlow.ELLIPSIS, font).width;
+}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/InlineFlow.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.InlineFlow;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwt.DWT;
+import dwtx.draw2d.Border;
+import dwtx.draw2d.ColorConstants;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.text.FlowFigure;
+import dwtx.draw2d.text.FlowFigureLayout;
+import dwtx.draw2d.text.FlowBox;
+import dwtx.draw2d.text.InlineFlowLayout;
+import dwtx.draw2d.text.FlowBorder;
+
+/**
+ * A <code>FlowFigure</code> represented by multiple <code>LineBox</code> fragments. An
+ * <code>InlineFlow</code>'s parent must be either a {@link BlockFlow} or another
+ * InlineFlow.
+ *
+ * <P>An InlineFlow may contain other InlineFlow figures.
+ *
+ * <P>WARNING: This class is not intended to be subclassed by clients.
+ * @author Randy Hudson
+ * @since 2.0
+ */
+public class InlineFlow : FlowFigure {
+
+List fragments;
+
+public this(){
+    fragments = new ArrayList(1);
+}
+
+/**
+ * Iterates over the children to find the width before a line-break is encountered.
+ * @see dwtx.draw2d.text.FlowFigure#addLeadingWordRequirements(int[])
+ */
+public bool addLeadingWordRequirements(int[] width) {
+    Iterator iter = getChildren().iterator();
+    while (iter.hasNext()) {
+        if ((cast(FlowFigure)iter.next()).addLeadingWordRequirements(width))
+            return true;
+    }
+    return false;
+}
+
+/**
+ * Extended to return false if the point is not also contained by at least one fragment.
+ * @return <code>true</code> if a fragment contains the given point
+ * @param x the relative x coordinate
+ * @param y the relative y coordinate
+ */
+public bool containsPoint(int x, int y) {
+    if (super.containsPoint(x, y)) {
+        List frags = getFragments();
+        for (int i = 0; i < frags.size(); i++)
+            if ((cast(FlowBox)frags.get(i)).containsPoint(x, y))
+                return true;
+    }
+
+    return false;
+}
+
+/**
+ * @see FlowFigure#createDefaultFlowLayout()
+ */
+protected FlowFigureLayout createDefaultFlowLayout() {
+    return new InlineFlowLayout(this);
+}
+
+/**
+ * Returns the <code>FlowBox</code> fragments contained in this InlineFlow.  The returned
+ * list should not be modified.
+ * @return The fragments
+ */
+public List getFragments() {
+    return fragments;
+}
+
+/**
+ * Overridden to paint a {@link FlowBorder} if present, and draw selection. The border is
+ * painted first, followed by selection which is generally done in XOR, which still allows
+ * the border to be seen.
+ * @param graphics the graphics
+ */
+protected void paintBorder(Graphics graphics) {
+    if (getBorder() !is null) {
+        FlowBorder fb = cast(FlowBorder)getBorder();
+        List frags = getFragments();
+        Rectangle where = new Rectangle();
+        int sides;
+        for (int i = 0; i < frags.size(); i++) {
+            FlowBox box = cast(FlowBox)frags.get(i);
+
+            where.x = box.getX();
+            where.width = box.getWidth();
+            where.y = -box.getAscentWithBorder();
+            where.height = box.getDescentWithBorder() - where.y;
+            where.y += box.getBaseline();
+            sides = 0;
+            if (i is 0)
+                sides = DWT.LEAD;
+            if (i is frags.size() - 1)
+                sides |= DWT.TRAIL;
+            fb.paint(this, graphics, where, sides);
+        }
+        graphics.restoreState();
+    }
+    if (selectionStart !is -1)
+        paintSelection(graphics);
+}
+
+/**
+ * Renders the XOR selection rectangles to the graphics.
+ * @param graphics the graphics to paint on
+ * @since 3.1
+ */
+protected void paintSelection(Graphics graphics) {
+    graphics.restoreState();
+    graphics.setXORMode(true);
+    graphics.setBackgroundColor(ColorConstants.white);
+    List list = getFragments();
+    FlowBox box;
+    for (int i = 0; i < list.size(); i++) {
+        box = cast(FlowBox)list.get(i);
+        int top = box.getLineRoot().getVisibleTop();
+        int bottom = box.getLineRoot().getVisibleBottom();
+        graphics.fillRectangle(box.getX(), top, box.getWidth(), bottom - top);
+    }
+}
+
+/**
+ * @see FlowFigure#postValidate()
+ */
+public void postValidate() {
+    List list = getFragments();
+    FlowBox box;
+    int left = Integer.MAX_VALUE, top = left;
+    int right = Integer.MIN_VALUE, bottom = right;
+
+    for (int i = 0; i < list.size(); i++) {
+        box = cast(FlowBox)list.get(i);
+        left = Math.min(left, box.getX());
+        right = Math.max(right, box.getX() + box.getWidth());
+        top = Math.min(top, box.getLineRoot().getVisibleTop());
+        bottom = Math.max(bottom, box.getLineRoot().getVisibleBottom());
+    }
+
+    setBounds(new Rectangle(left, top, right - left, bottom - top));
+    repaint();
+    list = getChildren();
+    for (int i = 0; i < list.size(); i++)
+        (cast(FlowFigure)list.get(i)).postValidate();
+}
+
+/**
+ * Overridden to assert that only {@link FlowBorder} is used. <code>null</code> is still a
+ * valid value as well.
+ * @param  border <code>null</code> or a FlowBorder
+ */
+public void setBorder(Border border) {
+    if (border is null || null !is cast(FlowBorder)border )
+        super.setBorder(border);
+    else
+        throw new RuntimeException("Border must be an instance of FlowBorder"); //$NON-NLS-1$
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/InlineFlowLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.InlineFlowLayout;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.draw2d.text.FlowContainerLayout;
+import dwtx.draw2d.text.FlowFigure;
+import dwtx.draw2d.text.LineBox;
+import dwtx.draw2d.text.CompositeBox;
+import dwtx.draw2d.text.NestedLine;
+import dwtx.draw2d.text.InlineFlow;
+
+/**
+ * The layout manager for {@link InlineFlow} figures.
+ *
+ * <P>WARNING: This class is not intended to be subclassed by clients.
+ * @author hudsonr
+ * @since 2.1
+ */
+public class InlineFlowLayout
+    : FlowContainerLayout
+{
+
+/**
+ * Creates a new InlineFlowLayout with the given FlowFigure.
+ * @param flow The FlowFigure
+ */
+public this(FlowFigure flow) {
+    super(flow);
+}
+
+/**
+ * Adds the given box as a line below the current line.
+ * @param box the box to add
+ */
+public void addLine(CompositeBox box) {
+    endLine();
+    getContext().addLine(box);
+}
+
+/**
+ * @see FlowContainerLayout#createNewLine()
+ */
+protected void createNewLine() {
+    currentLine = new NestedLine(cast(InlineFlow)getFlowFigure());
+    setupLine(currentLine);
+}
+
+/**
+ * @see FlowContext#endLine()
+ */
+public void endLine() {
+    flush();
+    getContext().endLine();
+}
+
+/**
+ * @see FlowContainerLayout#flush()
+ */
+protected void flush() {
+    if (currentLine !is null && currentLine.isOccupied()) {
+        // We want to preserve the state when a linebox is being added
+        bool sameLine = getContext().getContinueOnSameLine();
+        getContext().addToCurrentLine(currentLine);
+        (cast(InlineFlow)getFlowFigure()).getFragments().add(currentLine);
+        currentLine = null;
+        getContext().setContinueOnSameLine(sameLine);
+    }
+}
+
+/**
+ * InlineFlowLayout gets this information from its context.
+ * @see FlowContext#getContinueOnSameLine()
+ */
+public bool getContinueOnSameLine() {
+    return getContext().getContinueOnSameLine();
+}
+
+/**
+ * @see FlowContext#getWidthLookahead(FlowFigure, int[])
+ */
+public void getWidthLookahead(FlowFigure child, int result[]) {
+    List children = getFlowFigure().getChildren();
+    int index = -1;
+    if (child !is null)
+        index = children.indexOf(child);
+
+    for (int i = index + 1; i < children.size(); i++)
+        if ((cast(FlowFigure)children.get(i)).addLeadingWordRequirements(result))
+            return;
+
+    getContext().getWidthLookahead(getFlowFigure(), result);
+}
+
+/**
+ * @see FlowContainerLayout#isCurrentLineOccupied()
+ */
+public bool isCurrentLineOccupied() {
+    return (currentLine !is null && !currentLine.getFragments().isEmpty())
+        || getContext().isCurrentLineOccupied();
+}
+
+/**
+ * Clears out all fragments prior to the call to layoutChildren().
+ */
+public void preLayout() {
+    (cast(InlineFlow)getFlowFigure()).getFragments().clear();
+}
+
+/**
+ * InlineFlow passes this information to its context.
+ * @see FlowContext#setContinueOnSameLine(bool)
+ */
+public void setContinueOnSameLine(bool value) {
+    getContext().setContinueOnSameLine(value);
+}
+
+/**
+ * Initializes the given LineBox. Called by createNewLine().
+ * @param line The LineBox to initialize.
+ */
+protected void setupLine(LineBox line) {
+    line.setX(0);
+    line.setRecommendedWidth(getContext().getRemainingLineWidth());
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/LineBox.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.LineBox;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.draw2d.text.CompositeBox;
+import dwtx.draw2d.text.FlowBox;
+
+/**
+ * @author hudsonr
+ * @since 2.1
+ */
+public abstract class LineBox
+    : CompositeBox
+{
+
+/**
+ * The maximum ascent of all contained fragments.
+ */
+int contentAscent;
+
+/**
+ * The maximum descent of all contained fragments.
+ */
+int contentDescent;
+
+List fragments;
+
+public this(){
+    fragments = new ArrayList();
+}
+
+/**
+ * @see dwtx.draw2d.text.CompositeBox#add(dwtx.draw2d.text.FlowBox)
+ */
+public void add(FlowBox child) {
+    fragments.add(child);
+    width += child.getWidth();
+    contentAscent = Math.max(contentAscent, child.getOuterAscent());
+    contentDescent = Math.max(contentDescent, child.getOuterDescent());
+}
+
+/**
+ * @see dwtx.draw2d.text.FlowBox#getAscent()
+ */
+public int getAscent() {
+    int ascent = 0;
+    for (int i = 0; i < fragments.size(); i++)
+        ascent = Math.max(ascent, (cast(FlowBox)fragments.get(i)).getAscent());
+    return ascent;
+}
+
+/**
+ * Returns the remaining width available for line content.
+ * @return the available width in pixels
+ */
+int getAvailableWidth() {
+    if (recommendedWidth < 0)
+        return Integer.MAX_VALUE;
+    return recommendedWidth - getWidth();
+}
+
+int getBottomMargin() {
+    return 0;
+}
+
+/**
+ * @see dwtx.draw2d.text.FlowBox#getDescent()
+ */
+public int getDescent() {
+    int descent = 0;
+    for (int i = 0; i < fragments.size(); i++)
+        descent = Math.max(descent, (cast(FlowBox)fragments.get(i)).getDescent());
+    return descent;
+}
+
+/**
+ * @return Returns the fragments.
+ */
+List getFragments() {
+    return fragments;
+}
+
+int getTopMargin() {
+    return 0;
+}
+
+/**
+ * @return <code>true</code> if this box contains any fragments
+ */
+public bool isOccupied() {
+    return !fragments.isEmpty();
+}
+
+/**
+ * @see dwtx.draw2d.text.FlowBox#requiresBidi()
+ */
+public bool requiresBidi() {
+    for (Iterator iter = getFragments().iterator(); iter.hasNext();) {
+        FlowBox box = cast(FlowBox)iter.next();
+        if (box.requiresBidi())
+            return true;
+    }
+    return false;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/LineRoot.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,275 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.text.LineRoot;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.text.LineBox;
+import dwtx.draw2d.text.FlowBox;
+import dwtx.draw2d.text.NestedLine;
+import dwtx.draw2d.text.ContentBox;
+import dwtx.draw2d.text.InlineFlow;
+
+/**
+ * LineRoot is the top-most container on a line of text displayed in Draw2d.  Hence, a
+ * LineRoot can tell you of things like the highest ascent or descent on a line, which
+ * is required to display selection and such.  All
+ * {@link dwtx.draw2d.text.ContentBox fragments} know of the LineRoot they belong
+ * to.
+ * @author Randy Hudson
+ * @author Pratik Shah
+ * @since 3.1
+ */
+public class LineRoot
+    : LineBox
+{
+
+private int baseline;
+private bool isMirrored;
+
+/**
+ * Constructor
+ * @param isMirrored <code>true</code> if the line is to be displayed in a mirrored control
+ */
+public this(bool isMirrored) {
+    this.isMirrored = isMirrored;
+}
+
+/**
+ * @see dwtx.draw2d.text.CompositeBox#add(dwtx.draw2d.text.FlowBox)
+ */
+public void add(FlowBox child) {
+    super.add(child);
+    child.setLineRoot(this);
+}
+
+private void bidiCommit() {
+    int xLocation = getX();
+    BidiLevelNode root = new BidiLevelNode();
+    List branches = new ArrayList();
+    // branches does not include this LineRoot; all the non-leaf child fragments of a
+    // parent will be listed before the parent itself in this list
+    buildBidiTree(this, root, branches);
+    List result = new ArrayList();
+    root.emit(result);
+    int i = isMirrored ? result.size() - 1 : 0;
+    while (i >= 0 && i < result.size()) {
+        FlowBox box = cast(FlowBox)result.get(i);
+        box.setX(xLocation);
+        xLocation += box.getWidth();
+        i += isMirrored ? -1 : 1;
+    }
+    // set the bounds of the composite boxes, and break overlapping ones into two
+    layoutNestedLines(branches);
+}
+
+private void buildBidiTree(FlowBox box, BidiLevelNode node, List branches) {
+    if ( auto lb = cast(LineBox)box ) {
+        List children = lb.getFragments();
+        for (int i = 0; i < children.size(); i++)
+            buildBidiTree(cast(FlowBox)children.get(i), node, branches);
+        if (box !is this)
+            branches.add(box);
+    } else {
+        ContentBox leafBox = cast(ContentBox)box;
+        while (leafBox.getBidiLevel() < node.level)
+            node = node.pop();
+        while (leafBox.getBidiLevel() > node.level)
+            node = node.push();
+        node.add(leafBox);
+    }
+}
+
+/**
+ * Committing a LineRoot will position its children correctly. All children boxes are made
+ * to have the same baseline, and are laid out according to the Unicode BiDi Algorithm,
+ * or left-to-right if Bidi is not necessary.
+ */
+public void commit() {
+    if (requiresBidi())
+        bidiCommit();
+    else
+        contiguousCommit(this, getX());
+}
+
+/**
+ * A LineRoot cannot be targetted.
+ * @see dwtx.draw2d.text.FlowBox#containsPoint(int, int)
+ */
+public bool containsPoint(int x, int y) {
+    return false;
+}
+
+/*
+ * Simply lays out all fragments from left-to-right in the order in which they're
+ * contained.
+ */
+private void contiguousCommit(FlowBox box, int x) {
+    box.setX(x);
+    if (auto lb = cast(LineBox)box ) {
+        List fragments = lb.getFragments();
+        int i = isMirrored ? fragments.size() - 1 : 0;
+        while (i >= 0 && i < fragments.size()) {
+            FlowBox child = cast(FlowBox)fragments.get(i);
+            contiguousCommit(child, x);
+            x += child.getWidth();
+            i += isMirrored ? -1 : 1;
+        }
+    }
+}
+
+private Result findParent(NestedLine line, List branches, int afterIndex) {
+    for (int i = afterIndex + 1; i < branches.size(); i++) {
+        NestedLine box = cast(NestedLine)branches.get(i);
+        int index = box.getFragments().indexOf(line);
+        if (index >= 0)
+            return new Result(box, index);
+    }
+    return new Result(this, getFragments().indexOf(line));
+}
+
+/**
+ * @see dwtx.draw2d.text.FlowBox#getBaseline()
+ */
+public int getBaseline() {
+    return baseline;
+}
+
+LineRoot getLineRoot() {
+    return this;
+}
+
+int getVisibleBottom() {
+    return baseline + contentDescent;
+}
+
+int getVisibleTop() {
+    return baseline - contentAscent;
+}
+
+private void layoutNestedLines(List branches) {
+    for (int i = 0; i < branches.size(); i++) {
+        NestedLine parent = cast(NestedLine)branches.get(i);
+        FlowBox prevChild = null;
+        Rectangle bounds = null;
+        List frags = parent.getFragments();
+        for (int j = 0; j < frags.size(); j++) {
+            FlowBox child = cast(FlowBox) frags.get(j);
+            if (prevChild !is null && prevChild.getX() + prevChild.width !is child.getX()
+                    && child.getX() + child.width !is prevChild.getX()) {
+                // the boxes are not adjacent, and hence the parent box needs to
+                // be broken up
+                InlineFlow parentFig = parent.owner;
+                // Create and initialize a new line box
+                NestedLine newBox = new NestedLine(parentFig);
+                newBox.setLineRoot(this);
+                // Add all remaining fragments from the current line box to the new one
+                for (int k = j; k < frags.size();)
+                    newBox.fragments.add(frags.remove(k));
+                // Add the new line box to the parent box's list of fragments
+                Result result = findParent(parent, branches, i);
+                result.parent.getFragments().add(result.index + 1, newBox);
+                // Add the new line box to the flow figure's list of fragments
+                parentFig.fragments.add(parentFig.fragments.indexOf(parent) + 1, newBox);
+                branches.add(i + 1, newBox);
+                break;
+            }
+            if (bounds is null)
+                bounds = new Rectangle(child.getX(), 1, child.getWidth(), 1);
+            else
+                bounds.union_(child.getX(), 1, child.getWidth(), 1);
+            prevChild = child;
+        }
+        parent.setX(bounds.x);
+        parent.setWidth(bounds.width);
+    }
+}
+
+/**
+ * Positions the line vertically by settings its baseline.
+ * @param baseline the baseline
+ */
+public void setBaseline(int baseline) {
+    this.baseline = baseline;
+}
+
+/**
+ * @see dwtx.draw2d.text.CompositeBox#setLineTop(int)
+ */
+public void setLineTop(int top) {
+    this.baseline = top + getAscent();
+}
+
+private static class BidiLevelNode : ArrayList
+{
+    int level;
+    final BidiLevelNode parent;
+
+    this() {
+        this(null, 0);
+    }
+
+    this(BidiLevelNode parent, int level) {
+        this.parent = parent;
+        this.level = level;
+    }
+
+    void emit(List list) {
+        if (level % 2 is 1) {
+            for (int i = size() - 1; i >= 0; i--) {
+                Object child = get(i);
+                if (auto bln = cast(BidiLevelNode)child )
+                    bln.emit(list);
+                else
+                    list.add(child);
+            }
+        } else {
+            for (int i = 0; i < size(); i++) {
+                Object child = get(i);
+                if (auto bln = cast(BidiLevelNode)child )
+                    bln.emit(list);
+                else
+                    list.add(child);
+            }
+        }
+    }
+
+    BidiLevelNode pop() {
+        return parent;
+    }
+
+    BidiLevelNode push() {
+        if (!isEmpty()) {
+            Object last = get(size() - 1);
+            if (null !is cast(BidiLevelNode)last && (cast(BidiLevelNode)last).level is level + 1)
+                return cast(BidiLevelNode)last;
+        }
+        BidiLevelNode child = new BidiLevelNode(this, level + 1);
+        add(child);
+        return child;
+    }
+}
+
+private static class Result {
+    private int index;
+    private LineBox parent;
+    private this(LineBox box, int i) {
+        parent = box;
+        index = i;
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/NestedLine.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.text.NestedLine;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.text.LineBox;
+import dwtx.draw2d.text.LineRoot;
+import dwtx.draw2d.text.InlineFlow;
+import dwtx.draw2d.text.FlowUtilities;
+import dwtx.draw2d.text.FlowBox;
+
+/**
+ * @since 3.1
+ */
+public class NestedLine : LineBox {
+
+InlineFlow owner;
+private LineRoot root;
+
+this(InlineFlow owner) {
+    this.owner = owner;
+}
+
+/**
+ * @see dwtx.draw2d.text.FlowBox#containsPoint(int, int)
+ */
+public bool containsPoint(int x, int y) {
+    //$TODO should contains use LineRoot?
+    return x >= getX()
+        && x < getX() + getWidth()
+        && y >= getBaseline() - getAscentWithBorder()
+        && y < getBaseline() + getDescentWithBorder();
+}
+
+int getAscentWithBorder() {
+    return contentAscent + FlowUtilities.getBorderAscent(owner);
+}
+
+int getDescentWithBorder() {
+    return contentDescent + FlowUtilities.getBorderDescent(owner);
+}
+
+/**
+ * @see dwtx.draw2d.text.FlowBox#getBaseline()
+ */
+public int getBaseline() {
+    return root.getBaseline();
+}
+
+LineRoot getLineRoot() {
+    return root;
+}
+
+//int getVisibleAscent() {
+//  return contentAscent + FlowUtilities.getBorderAscent(owner);
+//}
+//
+//int getVisibleDescent() {
+//  return contentDescent + FlowUtilities.getBorderDescent(owner);
+//}
+
+/**
+ * Returns the outer ascent of this box.  The outer ascent is the ascent above the
+ * baseline including the border size and margin.  This is used when adding content into a
+ * LineBox.  The linebox's own border must be drawn around the children.
+ * @return the outer ascent of this box
+ */
+public int getOuterAscent() {
+    return contentAscent + FlowUtilities.getBorderAscentWithMargin(owner);
+}
+
+/**
+ * Returns the outer descent of this box. The outer descent is the space below the
+ * baseline including the border size and margin. This is used when adding content into a
+ * LineBox. The linebox's own border must be drawn around the children.
+ * @return the outer descent of this box
+ */
+public int getOuterDescent() {
+    return contentDescent + FlowUtilities.getBorderDescentWithMargin(owner);
+}
+
+void setLineRoot(LineRoot root) {
+    this.root = root;
+    for (int i = 0; i < fragments.size(); i++)
+        (cast(FlowBox)fragments.get(i)).setLineRoot(root);
+}
+
+/**
+ * @see dwtx.draw2d.text.CompositeBox#setLineTop(int)
+ */
+public void setLineTop(int top) {
+    throw new RuntimeException("not supported"); //$NON-NLS-1$
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/PageFlowLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.PageFlowLayout;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.text.BlockFlowLayout;
+import dwtx.draw2d.text.FlowPage;
+
+/**
+ * A block layout which requires no FlowContext to perform its layout. This class is used
+ * by {@link FlowPage}.
+ * <p>
+ * WARNING: This class is not intended to be subclassed by clients.
+ */
+public class PageFlowLayout
+    : BlockFlowLayout
+{
+
+/**
+ * Creates a new PageFlowLayout with the given FlowPage
+ * @param page the FlowPage
+ */
+public this(FlowPage page) {
+    super(page);
+}
+
+/**
+ * @see dwtx.draw2d.text.BlockFlowLayout#getContextWidth()
+ */
+int getContextWidth() {
+    return (cast(FlowPage)getFlowFigure()).getPageWidth();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/ParagraphTextLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,218 @@
+/*******************************************************************************
+ * 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.ParagraphTextLayout;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwt.graphics.Font;
+import dwtx.draw2d.text.TextLayout;
+import dwtx.draw2d.text.TextFlow;
+import dwtx.draw2d.text.FlowUtilities;
+import dwtx.draw2d.text.FlowContext;
+import dwtx.draw2d.text.TextFragmentBox;
+import dwtx.draw2d.text.FlowBorder;
+
+/**
+ * The layout for {@link TextFlow}.
+ * @author hudsonr
+ * @since 2.1
+ */
+public class ParagraphTextLayout
+    : TextLayout
+{
+
+/**
+ * Wrapping will ONLY occur at valid line breaks
+ */
+public static const int WORD_WRAP_HARD = 0;
+
+/**
+ * Wrapping will always occur at the end of the available space, breaking in the middle of
+ * a word.
+ */
+public static const int WORD_WRAP_SOFT = 1;
+
+/**
+ * Wrapping will always occur at the end of available space, truncating a word if it
+ * doesn't fit.  Note that truncation is not supported across multiple figures and
+ * with BiDi.  Undesired effects may result if that is the case.
+ */
+public static const int WORD_WRAP_TRUNCATE = 2;
+
+private int wrappingStyle = WORD_WRAP_HARD;
+
+/**
+ * Constructs a new ParagraphTextLayout on the specified TextFlow.
+ * @param flow the TextFlow
+ */
+public this(TextFlow flow) {
+    super(flow);
+}
+
+/**
+ * Constructs the layout with the specified TextFlow and wrapping style.  The wrapping
+ * style must be one of:
+ * <UL>
+ *  <LI>{@link #WORD_WRAP_HARD}</LI>
+ *  <LI>{@link #WORD_WRAP_SOFT}</LI>
+ *  <LI>{@link #WORD_WRAP_TRUNCATE}</LI>
+ * </UL>
+ * @param flow the textflow
+ * @param style the style of wrapping
+ */
+public this(TextFlow flow, int style) {
+    this(flow);
+    wrappingStyle = style;
+}
+
+/**
+ * Given the Bidi levels of the given text, this method breaks the given text up by
+ * its level runs.
+ * @param text the String that needs to be broken up into its level runs
+ * @param levelInfo the Bidi levels
+ * @return the requested segment
+ */
+private String[] getSegments(String text, int levelInfo[]) {
+    if (levelInfo.length is 1)
+        return [text];
+
+    String result[] = new String[levelInfo.length / 2 + 1];
+
+    int i;
+    int endOffset;
+    int beginOffset = 0;
+
+    for (i = 0; i < result.length - 1; i++) {
+        endOffset = levelInfo[i * 2 + 1];
+        result[i] = text.substring(beginOffset, endOffset);
+        beginOffset = endOffset;
+    }
+    endOffset = text.length;
+    result[i] = text.substring(beginOffset, endOffset);
+    return result;
+}
+
+class SegmentLookahead : /+FlowUtilities.+/LookAhead {
+    private int seg = -1;
+    private String segs[];
+    private int[] width;
+    private final int trailingBorderSize;
+    this(String segs[], int trailingBorderSize) {
+        this.segs = segs;
+        this.trailingBorderSize = trailingBorderSize;
+    }
+    public int getWidth() {
+        if (width is null) {
+            width = new int[1];
+            int startingIndex = seg + 1;
+            TextFlow textFlow = cast(TextFlow)getFlowFigure();
+
+            if (startingIndex is segs.length) {
+                width[0] += trailingBorderSize;
+                getContext().getWidthLookahead(textFlow, width);
+            } else {
+                String rest = segs[startingIndex];
+                for (int k = startingIndex + 1; k < segs.length; k++)
+                    rest ~= segs[k];
+                if (!textFlow.addLeadingWordWidth(rest, width)) {
+                    width[0] += trailingBorderSize;
+                    getContext().getWidthLookahead(textFlow, width);
+                }
+            }
+        }
+        return width[0];
+    }
+    public void setIndex(int value) {
+        this.seg = value;
+        width = null;
+    }
+}
+
+/**
+ * @see dwtx.draw2d.text.FlowFigureLayout#layout()
+ */
+protected void layout() {
+    TextFlow textFlow = cast(TextFlow)getFlowFigure();
+    int offset = 0;
+
+    FlowContext context = getContext();
+    List fragments = textFlow.getFragments();
+    Font font = textFlow.getFont();
+    int fragIndex = 0;
+    int advance = 0;
+
+    TextFragmentBox fragment;
+    int levelInfo[] = (textFlow.getBidiInfo() is null)
+        ? [-1]
+        : textFlow.getBidiInfo().levelInfo;
+
+    String segment;
+    String[] segments = getSegments(textFlow.getText(), levelInfo);
+    FlowBorder border = null;
+    if ( null !is cast(FlowBorder)textFlow.getBorder() )
+        border = cast(FlowBorder)textFlow.getBorder();
+
+    SegmentLookahead lookahead = new SegmentLookahead(segments, border is null ? 0 : border.getRightMargin());
+    int seg;
+
+    if (border !is null) {
+        fragment = getFragment(fragIndex++, fragments);
+        fragment.setBidiLevel(levelInfo[0]);
+        fragment.setTruncated(false);
+        fragment.offset = fragment.length = -1;
+        fragment.setWidth(border.getLeftMargin() + border.getInsets(textFlow).left);
+        if (context.getRemainingLineWidth()
+                < fragment.getWidth() + lookahead.getWidth())
+            context.endLine();
+        context.addToCurrentLine(fragment);
+    }
+
+    FlowUtilities flowUtilities = textFlow.getFlowUtilities_package();
+    for (seg = 0; seg < segments.length; seg++) {
+        segment = segments[seg];
+        lookahead.setIndex(seg);
+
+        do {
+            fragment = getFragment(fragIndex++, fragments);
+
+            fragment.offset = offset;
+            fragment.setBidiLevel(levelInfo[seg * 2]);
+
+            advance = flowUtilities.wrapFragmentInContext_package(fragment, segment,
+                    context, lookahead, font, wrappingStyle);
+            segment = segment.substring(advance);
+            offset += advance;
+            if ((segment.length > 0
+                    || fragment.length < advance)
+                    || fragment.isTruncated())
+                context.endLine();
+        } while (segment.length > 0
+                || (!fragment.isTruncated() && fragment.length < advance));
+    }
+
+    if (border !is null) {
+        fragment = getFragment(fragIndex++, fragments);
+        fragment.setBidiLevel(levelInfo[0]);
+        fragment.setTruncated(false);
+        fragment.offset = fragment.length = -1;
+        fragment.setWidth(border.getRightMargin() + border.getInsets(textFlow).right);
+        context.addToCurrentLine(fragment);
+    }
+
+    //Remove the remaining unused fragments.
+    while (fragIndex < fragments.size())
+        fragments.remove(fragments.size() - 1);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/SimpleTextLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.SimpleTextLayout;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwt.graphics.Font;
+import dwtx.draw2d.text.TextLayout;
+import dwtx.draw2d.text.TextFlow;
+import dwtx.draw2d.text.TextFragmentBox;
+import dwtx.draw2d.text.FlowUtilities;
+
+/**
+ * @author hudsonr
+ * @since 2.1
+ */
+public class SimpleTextLayout : TextLayout {
+
+private static const String[] DELIMITERS = [
+    "\r\n", //$NON-NLS-1$
+     "\n", //$NON-NLS-1$
+     "\r"];//$NON-NLS-1$
+
+private static int result;
+private static int delimeterLength;
+
+/**
+ * Creates a new SimpleTextLayout with the given TextFlow
+ * @param flow the TextFlow
+ */
+public this(TextFlow flow) {
+    super (flow);
+}
+
+/**
+ * @see dwtx.draw2d.text.FlowFigureLayout#layout()
+ */
+protected void layout() {
+    TextFlow textFlow = cast(TextFlow)getFlowFigure();
+    String text = textFlow.getText();
+    List fragments = textFlow.getFragments();
+    Font font = textFlow.getFont();
+    TextFragmentBox fragment;
+    int i = 0;
+    int offset = 0;
+    FlowUtilities flowUtilities = textFlow.getFlowUtilities_package();
+
+    do {
+        nextLineBreak(text, offset);
+        fragment = getFragment(i++, fragments);
+        fragment.length = result - offset;
+        fragment.offset = offset;
+        fragment.setWidth(-1);
+        flowUtilities.setupFragment_package(fragment, font, text.substring(offset, result));
+        getContext().addToCurrentLine(fragment);
+        getContext().endLine();
+        offset = result + delimeterLength;
+    } while (offset < text.length);
+    //Remove the remaining unused fragments.
+    while (i < fragments.size())
+        fragments.remove(i++);
+}
+
+private int nextLineBreak(String text, int offset) {
+    result = text.length;
+    delimeterLength = 0;
+    int current;
+    for (int i = 0; i < DELIMITERS.length; i++) {
+        current = text.indexOf(DELIMITERS[i], offset);
+        if (current !is -1 && current < result) {
+            result = current;
+            delimeterLength = DELIMITERS[i].length;
+        }
+    }
+    return result;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/TextFlow.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,686 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.TextFlow;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwt.DWT;
+import dwt.graphics.Color;
+import dwt.graphics.TextLayout;
+import dwtx.draw2d.ColorConstants;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.TextUtilities;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.text.InlineFlow;
+import dwtx.draw2d.text.BidiInfo;
+import dwtx.draw2d.text.BidiProcessor;
+import dwtx.draw2d.text.FlowFigureLayout;
+import dwtx.draw2d.text.TextFragmentBox;
+import dwtx.draw2d.text.CaretInfo;
+import dwtx.draw2d.text.FlowUtilities;
+import dwtx.draw2d.text.ParagraphTextLayout;
+import dwtx.draw2d.text.BidiChars;
+import dwtx.draw2d.text.FlowBorder;
+
+import tango.text.convert.Format;
+
+/**
+ * An inline flow figure that renders a string of text across one or more lines. A
+ * TextFlow cannot contain children. All <code>InlineFlow</code> figure's must be
+ * parented by a <code>FlowFigure</code>.
+ * <p>
+ * WARNING: This class is not intended to be subclassed by clients.
+ * @author hudsonr
+ * @author Pratik Shah
+ * @since 2.1
+ */
+public class TextFlow
+    : InlineFlow
+{
+
+static final String ELLIPSIS = "..."; //$NON-NLS-1$
+private BidiInfo bidiInfo;
+private int selectionEnd = -1;
+private String text;
+
+/**
+ * Constructs a new TextFlow with the empty String.
+ * @see java.lang.Object#Object()
+ */
+public this() {
+    this(""/+new String()+/);
+}
+
+/**
+ * Constructs a new TextFlow with the specified String.
+ * @param s the string
+ */
+public this(String s) {
+    text = s;
+}
+
+/**
+ * Returns the width of the text until the first line-break.
+ * @see dwtx.draw2d.text.FlowFigure#addLeadingWordRequirements(int[])
+ */
+public bool addLeadingWordRequirements(int[] width) {
+    return addLeadingWordWidth(getText(), width);
+}
+
+/**
+ * Calculates the width taken up by the given text before a line-break is encountered.
+ *
+ * @param text the text in which the break is to be found
+ * @param width the width before the next line-break (if one's found; the width of all
+ * the given text, otherwise) will be added on to the first int in the given array
+ * @return <code>true</code> if a line-break was found
+ * @since 3.1
+ */
+bool addLeadingWordWidth(String text, int[] width) {
+    if (text.length is 0)
+        return false;
+    if (CharacterIsWhitespace(text.firstCodePoint()))
+        return true;
+
+    text = 'a' ~ text ~ 'a';
+    FlowUtilities.LINE_BREAK.setText(text);
+    int index = FlowUtilities.LINE_BREAK.next() - 1;
+    if (index is 0)
+        return true;
+    while (CharacterIsWhitespace(text[index..$].firstCodePoint()))
+        index--;
+    bool result = index < text.length - 1;
+    // index should point to the end of the actual text (not including the 'a' that was
+    // appended), if there were no breaks
+    if (index is text.length - 1)
+        index--;
+    text = text.substring(1, index + 1);
+
+    if (bidiInfo is null)
+        width[0] += getTextUtilities().getStringExtents(text, getFont()).width;
+    else {
+        TextLayout textLayout = FlowUtilities.getTextLayout();
+        textLayout.setFont(getFont());
+        textLayout.setText(text);
+        width[0] += textLayout.getBounds().width;
+    }
+    return result;
+}
+
+/**
+ * A TextFlow contributes its text.
+ * @see dwtx.draw2d.text.FlowFigure#contributeBidi(dwtx.draw2d.text.BidiProcessor)
+ */
+protected void contributeBidi(BidiProcessor proc) {
+    bidiInfo = null;
+    proc.add(this, getText());
+}
+
+/**
+ * @see dwtx.draw2d.text.InlineFlow#createDefaultFlowLayout()
+ */
+protected FlowFigureLayout createDefaultFlowLayout() {
+    return new ParagraphTextLayout(this);
+}
+
+private int findNextLineOffset(Point p, int[] trailing) {
+    if (getBounds().bottom() <= p.y)
+        return -1;
+
+    TextFragmentBox closestBox = null;
+    int index = 0;
+    List fragments = getFragmentsWithoutBorder();
+    for (int i = fragments.size() - 1; i >= 0; i--) {
+        TextFragmentBox box = cast(TextFragmentBox)fragments.get(i);
+        if (box.getBaseline() - box.getLineRoot().getAscent() > p.y
+                && (closestBox is null
+                || box.getBaseline() < closestBox.getBaseline()
+                || (box.getBaseline() is closestBox.getBaseline()
+                && hDistanceBetween(box, p.x) < hDistanceBetween(closestBox, p.x)))) {
+            closestBox = box;
+            index = i;
+        }
+    }
+    return findOffset(p, trailing, closestBox, index);
+}
+
+private int findOffset(Point p, int[] trailing, TextFragmentBox box, int boxIndex) {
+    if (box is null)
+        return -1;
+    TextLayout layout = FlowUtilities.getTextLayout();
+    layout.setFont(getFont());
+    layout.setText(getBidiSubstring(box, boxIndex));
+    int x = p.x - box.getX();
+    if (isMirrored())
+        x = box.getWidth() - x;
+    int layoutOffset = layout.getOffset(x, p.y - box.getTextTop(), trailing);
+    return box.offset + layoutOffset - getBidiPrefixLength(box, boxIndex);
+}
+
+private int findPreviousLineOffset(Point p, int[] trailing) {
+    if (getBounds().y > p.y)
+        return -1;
+
+    TextFragmentBox closestBox = null;
+    int index = 0;
+    List fragments = getFragmentsWithoutBorder();
+    for (int i = fragments.size() - 1; i >= 0; i--) {
+        TextFragmentBox box = cast(TextFragmentBox)fragments.get(i);
+        if (box.getBaseline() + box.getLineRoot().getDescent() < p.y
+                && (closestBox is null
+                || box.getBaseline() > closestBox.getBaseline()
+                || (box.getBaseline() is closestBox.getBaseline()
+                && hDistanceBetween(box, p.x) < hDistanceBetween(closestBox, p.x)))) {
+            closestBox = box;
+            index = i;
+        }
+    }
+    return findOffset(p, trailing, closestBox, index);
+}
+
+int getAscent() {
+    return getTextUtilities().getAscent(getFont());
+}
+
+/**
+ * Returns the BidiInfo for this figure or <code>null</code>.
+ * @return <code>null</code> or the info
+ * @since 3.1
+ */
+public BidiInfo getBidiInfo() {
+    return bidiInfo;
+}
+
+private int getBidiPrefixLength(TextFragmentBox box, int index) {
+    if (box.getBidiLevel() < 1)
+        return 0;
+    if (index > 0 || !bidiInfo.leadingJoiner)
+        return 1;
+    return 2;
+}
+
+/**
+ * @param box which fragment
+ * @param index the fragment index
+ * @return the bidi string for that fragment
+ * @since 3.1
+ */
+protected String getBidiSubstring(TextFragmentBox box, int index) {
+    if (box.getBidiLevel() < 1)
+        return getText().substring(box.offset, box.offset + box.length);
+
+    StringBuffer buffer = new StringBuffer(box.length + 3);
+    buffer.append( dcharToString( box.isRightToLeft() ? BidiChars.RLO : BidiChars.LRO ));
+    if (index is 0 && bidiInfo.leadingJoiner)
+        buffer.append(dcharToString(BidiChars.ZWJ));
+    buffer.append(getText().substring(box.offset, box.offset + box.length));
+    if (index is getFragmentsWithoutBorder().size() - 1 && bidiInfo.trailingJoiner)
+        buffer.append(dcharToString(BidiChars.ZWJ));
+    return buffer.toString();
+}
+
+/**
+ * Returns the CaretInfo in absolute coordinates. The offset must be between 0 and the
+ * length of the String being displayed.
+ * @since 3.1
+ * @param offset the location in this figure's text
+ * @param trailing true if the caret is being placed after the offset
+ * @exception IllegalArgumentException If the offset is not between <code>0</code> and the
+ * length of the string inclusively
+ * @return the caret bounds relative to this figure
+ */
+public CaretInfo getCaretPlacement(int offset, bool trailing) {
+    if (offset < 0 || offset > getText().length)
+        throw new IllegalArgumentException(Format("Offset: {} is invalid", offset //$NON-NLS-1$
+                )); //$NON-NLS-1$
+
+    if (offset is getText().length)
+        trailing = false;
+
+    List fragments = getFragmentsWithoutBorder();
+    int i = fragments.size();
+    TextFragmentBox box;
+    do
+        box = cast(TextFragmentBox)fragments.get(--i);
+    while (offset < box.offset && i > 0);
+
+    // Cannot be trailing and after the last char, so go to first char in next box
+    if (trailing && box.offset + box.length <= offset) {
+        box = cast(TextFragmentBox)fragments.get(++i);
+        offset = box.offset;
+        trailing = false;
+    }
+
+    Point where = getPointInBox(box, offset, i, trailing);
+    CaretInfo info = new CaretInfo(where.x, where.y, box.getAscent(), box.getDescent(),
+            box.getLineRoot().getAscent(), box.getLineRoot().getDescent());
+    translateToAbsolute(info);
+    return info;
+}
+
+Point getPointInBox(TextFragmentBox box, int offset, int index, bool trailing) {
+    offset -= box.offset;
+    offset = Math.min(box.length, offset);
+    Point result = new Point(0, box.getTextTop());
+    if (bidiInfo is null) {
+        if (trailing && offset < box.length)
+            offset++;
+        String substring = getText().substring(box.offset, box.offset + offset);
+        result.x = getTextUtilities().getStringExtents(substring, getFont()).width;
+    } else {
+        TextLayout layout = FlowUtilities.getTextLayout();
+        layout.setFont(getFont());
+        String fragString = getBidiSubstring(box, index);
+        layout.setText(fragString);
+        offset += getBidiPrefixLength(box, index);
+        result.x = layout.getLocation(offset, trailing).x;
+        if (isMirrored())
+            result.x = box.width - result.x;
+    }
+    result.x += box.getX();
+    return result;
+}
+
+int getDescent() {
+    return getTextUtilities().getDescent(getFont());
+}
+
+/**
+ * Returns the minimum character offset which is on the given baseline y-coordinate. The y
+ * location should be relative to this figure. The return value will be between
+ * 0 and N-1.  If no fragment is located on the baseline, <code>-1</code> is returned.
+ * @since 3.1
+ * @param baseline the relative baseline coordinate
+ * @return -1 or the lowest offset for the line
+ */
+public int getFirstOffsetForLine(int baseline) {
+    TextFragmentBox box;
+    List fragments = getFragmentsWithoutBorder();
+    for (int i = 0; i < fragments.size(); i++) {
+        box = cast(TextFragmentBox)fragments.get(i);
+        if (baseline is box.getBaseline())
+            return box.offset;
+    }
+    return -1;
+}
+
+/**
+ * Returns the <code>TextFragmentBox</code> fragments contained in this TextFlow, not
+ * including the border fragments.  The returned list should not be modified.
+ * @return list of fragments without the border fragments
+ * @since 3.4
+ */
+protected List getFragmentsWithoutBorder() {
+    List fragments = getFragments();
+    if (getBorder() !is null)
+        fragments = fragments.subList(1, fragments.size() - 1);
+    return fragments;
+}
+
+/**
+ * Returns the maximum offset for a character which is on the given baseline y-coordinate.
+ * The y location should be relative to this figure. The return value will be between
+ * 0 and N-1.  If no fragment is located on the baseline, <code>-1</code> is returned.
+ * @since 3.1
+ * @param baseline the relative baseline coordinate
+ * @return -1 or the highest offset at the given baseline
+ */
+public int getLastOffsetForLine(int baseline) {
+    TextFragmentBox box;
+    List fragments = getFragmentsWithoutBorder();
+    for (int i = fragments.size() - 1; i >= 0; i--) {
+        box = cast(TextFragmentBox)fragments.get(i);
+        if (baseline is box.getBaseline())
+            return box.offset + box.length - 1;
+    }
+    return -1;
+}
+
+/**
+ * Returns the offset nearest the given point either up or down one line.  If no offset
+ * is found, -1 is returned.  <code>trailing[0]</code> will be set to 1 if the reference
+ * point is closer to the trailing edge of the offset than it is to the leading edge.
+ * @since 3.1
+ * @param p a reference point
+ * @param down <code>true</code> if the search is down
+ * @param trailing an int array
+ * @return the next offset or <code>-1</code>
+ */
+public int getNextOffset(Point p, bool down, int[] trailing) {
+    return down ? findNextLineOffset(p, trailing) : findPreviousLineOffset(p, trailing);
+}
+
+/**
+ * Returns the next offset which is visible in at least one fragment or -1 if there is
+ * not one. A visible offset means that the character or the one preceding it is
+ * displayed, which implies that a caret can be positioned at such an offset.  This is
+ * useful for advancing a caret past characters which resulted in a line wrap.
+ *
+ * @param offset the reference offset
+ * @return the next offset which is visible
+ * @since 3.1
+ */
+public int getNextVisibleOffset(int offset) {
+    TextFragmentBox box;
+    List fragments = getFragmentsWithoutBorder();
+    for (int i = 0; i < fragments.size(); i++) {
+        box = cast(TextFragmentBox)fragments.get(i);
+        if (box.offset + box.length <= offset)
+            continue;
+        return Math.max(box.offset, offset + 1);
+    }
+    return -1;
+}
+
+/**
+ * Returns the offset of the character directly below or nearest the given location. The
+ * point must be relative to this figure. The return value will be between 0 and N-1. If
+ * the proximity argument is not <code>null</code>, the result may also be <code>-1</code>
+ * if no offset was found within the proximity.
+ * <P>
+ * For a typical character, the trailing argument will be filled in to indicate whether
+ * the point is closer to the leading edge (0) or the trailing edge (1).  When the point
+ * is over a cluster composed of multiple characters, the trailing argument will be filled
+ * with the  position of the character in the cluster that is closest to the point.
+ * <P>
+ * If the proximity argument is not <code>null</code>, then the location may be no further
+ * than the proximity given.  Passing <code>null</code> is equivalent to passing <code>new
+ * Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE)</code>. The <code>width</code> field of
+ * the proximity will contain the horizontal distance, <code>height</code> will contain
+ * vertical. Vertical proximity is more important than horizontal. The returned offset is
+ * the lowest index with minimum vertical proximity not exceeding the given limit, with
+ * horizontal proximity not exceeding the given limit.  If an offset that is within the
+ * proximity is found, then the given <code>Dimension</code> will be updated to reflect
+ * the new proximity.
+ *
+ *
+ * @since 3.1
+ * @param p the point relative to this figure
+ * @param trailing the trailing buffer
+ * @param proximity restricts and records the distance of the returned offset
+ * @return the nearest offset in this figure's text
+ */
+public int getOffset(Point p, int trailing[], Dimension proximity) {
+    if (proximity is null)
+        proximity = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
+    TextFragmentBox closestBox = null;
+    int index = 0;
+    int dy;
+    int dx;
+    int i = 0;
+    int size = fragments.size();
+    if (null !is cast(FlowBorder)getBorder() ) {
+        i++;
+        size--;
+    }
+    for (; i < size; i++) {
+        TextFragmentBox box = cast(TextFragmentBox)fragments.get(i);
+        dy = vDistanceBetween(box, p.y);
+        if (dy > proximity.height)
+            continue;
+        dx = hDistanceBetween(box, p.x);
+        if (dy is proximity.height && dx >= proximity.width)
+                continue;
+        proximity.height = dy;
+        proximity.width = dx;
+        closestBox = box;
+        index = i;
+    }
+    return findOffset(p, trailing, closestBox, index);
+}
+
+/**
+ * Returns the previous offset which is visible in at least one fragment or -1 if there
+ * is not one. See {@link #getNextVisibleOffset(int)} for more.
+ *
+ * @param offset a reference offset
+ * @return -1 or the previous offset which is visible
+ * @since 3.1
+ */
+
+public int getPreviousVisibleOffset(int offset) {
+    TextFragmentBox box;
+    if (offset is -1)
+        offset = Integer.MAX_VALUE;
+    List fragments = getFragmentsWithoutBorder();
+    for (int i = fragments.size() - 1; i >= 0; i--) {
+        box = cast(TextFragmentBox)fragments.get(i);
+        if (box.offset >= offset)
+            continue;
+        return Math.min(box.offset + box.length, offset - 1);
+    }
+    return -1;
+}
+
+/**
+ * @return the String being displayed; will not be <code>null</code>
+ */
+public String getText() {
+    return text;
+}
+
+int getVisibleAscent() {
+    if (null !is cast(FlowBorder)getBorder() ) {
+        FlowBorder border = cast(FlowBorder)getBorder();
+        return border.getInsets(this).top + getAscent();
+    }
+    return getAscent();
+}
+
+int getVisibleDescent() {
+    if (null !is cast(FlowBorder)getBorder() ) {
+        FlowBorder border = cast(FlowBorder)getBorder();
+        return border.getInsets(this).bottom + getDescent();
+    }
+    return getDescent();
+}
+
+private int hDistanceBetween(TextFragmentBox box, int x) {
+    if (x < box.getX())
+        return box.getX() - x;
+    return Math.max(0, x - (box.getX() + box.getWidth()));
+}
+
+/**
+ * Returns <code>true</code> if a portion if the text is truncated using ellipses ("...").
+ * @return <code>true</code> if the text is truncated with ellipses
+ */
+public bool isTextTruncated() {
+    for (int i = 0; i < fragments.size(); i++) {
+        if ((cast(TextFragmentBox)fragments.get(i)).isTruncated())
+            return true;
+    }
+    return false;
+}
+
+/**
+ * @see dwtx.draw2d.Figure#paintFigure(Graphics)
+ */
+protected void paintFigure(Graphics g) {
+    TextFragmentBox frag;
+    g.getClip(Rectangle.SINGLETON);
+    int yStart = Rectangle.SINGLETON.y;
+    int yEnd = Rectangle.SINGLETON.bottom();
+
+    for (int i = 0; i < fragments.size(); i++) {
+        frag = cast(TextFragmentBox)fragments.get(i);
+//      g.drawLine(frag.getX(), frag.getLineRoot().getVisibleTop(),
+//              frag.getWidth() + frag.getX(), frag.getLineRoot().getVisibleTop());
+//      g.drawLine(frag.getX(), frag.getBaseline(), frag.getWidth() + frag.getX(), frag.getBaseline());
+        if (frag.offset is -1)
+            continue;
+        //Loop until first visible fragment
+        if (yStart > frag.getLineRoot().getVisibleBottom() + 1)//The + 1 is for disabled text
+            continue;
+        //Break loop at first non-visible fragment
+        if (yEnd < frag.getLineRoot().getVisibleTop())
+            break;
+
+        String draw = getBidiSubstring(frag, i);
+
+        if (frag.isTruncated())
+            draw ~= ELLIPSIS;
+
+        if (!isEnabled()) {
+            Color fgColor = g.getForegroundColor();
+            g.setForegroundColor(ColorConstants.buttonLightest);
+            paintText(g, draw,
+                    frag.getX() + 1,
+                    frag.getBaseline() - getAscent() + 1,
+                    frag.getBidiLevel());
+            g.setForegroundColor(ColorConstants.buttonDarker);
+            paintText(g, draw,
+                    frag.getX(),
+                    frag.getBaseline() - getAscent(),
+                    frag.getBidiLevel());
+            g.setForegroundColor(fgColor);
+        } else {
+            paintText(g, draw,
+                    frag.getX(),
+                    frag.getBaseline() - getAscent(),
+                    frag.getBidiLevel());
+        }
+    }
+}
+
+/**
+ * @see InlineFlow#paintSelection(dwtx.draw2d.Graphics)
+ */
+protected void paintSelection(Graphics graphics) {
+    if (selectionStart is -1)
+        return;
+    graphics.setXORMode(true);
+    graphics.setBackgroundColor(ColorConstants.white);
+
+    TextFragmentBox frag;
+    for (int i = 0; i < fragments.size(); i++) {
+        frag = cast(TextFragmentBox)fragments.get(i);
+        //Loop until first visible fragment
+        if (frag.offset + frag.length <= selectionStart)
+            continue;
+        if (frag.offset > selectionEnd)
+            return;
+        if (selectionStart <= frag.offset && selectionEnd >= frag.offset + frag.length) {
+            int y = frag.getLineRoot().getVisibleTop();
+            int height = frag.getLineRoot().getVisibleBottom() - y;
+            graphics.fillRectangle(frag.getX(), y, frag.getWidth(), height);
+        } else if (selectionEnd > frag.offset && selectionStart < frag.offset + frag.length) {
+            Point p1 = getPointInBox(frag, Math.max(frag.offset, selectionStart), i, false);
+            Point p2 = getPointInBox(frag, Math.min(frag.offset + frag.length, selectionEnd) - 1, i, true);
+            Rectangle rect = new Rectangle(p1, p2);
+            rect.width--;
+            rect.y = frag.getLineRoot().getVisibleTop();
+            rect.height = frag.getLineRoot().getVisibleBottom() - rect.y;
+            graphics.fillRectangle(rect);
+        }
+    }
+}
+
+protected void paintText(Graphics g, String draw, int x, int y, int bidiLevel) {
+    if (bidiLevel is -1) {
+        g.drawString(draw, x, y);
+    } else {
+        TextLayout tl = FlowUtilities.getTextLayout();
+        if (isMirrored())
+            tl.setOrientation(DWT.RIGHT_TO_LEFT);
+        tl.setFont(g.getFont());
+        tl.setText(draw);
+        g.drawTextLayout(tl, x, y);
+    }
+}
+
+/**
+ * @see dwtx.draw2d.text.FlowFigure#setBidiInfo(dwtx.draw2d.text.BidiInfo)
+ */
+public void setBidiInfo(BidiInfo info) {
+    this.bidiInfo = info;
+}
+
+/**
+ * Sets the extent of selection.  The selection range is inclusive.  For example, the
+ * range [0, 0] indicates that the first character is selected.
+ * @param start the start offset
+ * @param end the end offset
+ * @since 3.1
+ */
+public void setSelection(int start, int end) {
+    bool repaint_ = false;
+
+    if (selectionStart is start) {
+        if (selectionEnd is end)
+            return;
+        repaint_ = true;
+    } else
+        repaint_ = selectionStart !is selectionEnd || start !is end;
+
+    selectionStart = start;
+    selectionEnd = end;
+    if (repaint_)
+        repaint();
+}
+
+/**
+ * Sets the text being displayed. The string may not be <code>null</code>.
+ * @param s The new text
+ */
+public void setText(String s) {
+    if (s !is null && !s.equals(text)) {
+        text = s;
+        revalidateBidi(this);
+        repaint();
+    }
+}
+
+/**
+ * @see java.lang.Object#toString()
+ */
+public String toString() {
+    return text;
+}
+
+private int vDistanceBetween(TextFragmentBox box, int y) {
+    int top = box.getBaseline() - box.getLineRoot().getAscent();
+    if (y < top)
+        return top - y;
+    return Math.max(0, y - (box.getBaseline() + box.getLineRoot().getDescent()));
+}
+
+/**
+ * Gets the <code>FlowUtilities</code> instance to be used in measurement
+ * calculations.
+ *
+ * @return a <code>FlowUtilities</code> instance
+ * @since 3.4
+ */
+protected FlowUtilities getFlowUtilities() {
+    return FlowUtilities.INSTANCE;
+}
+package FlowUtilities getFlowUtilities_package() {
+    return getFlowUtilities();
+}
+
+/**
+ * Gets the <code>TextUtilities</code> instance to be used in measurement
+ * calculations.
+ *
+ * @return a <code>TextUtilities</code> instance
+ * @since 3.4
+ */
+protected TextUtilities getTextUtilities() {
+    return TextUtilities.INSTANCE;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/TextFragmentBox.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,134 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.TextFragmentBox;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.text.ContentBox;
+import dwtx.draw2d.text.TextFlow;
+import dwtx.draw2d.text.FlowUtilities;
+import tango.text.convert.Format;
+
+/**
+ * A Geometric object for representing a TextFragment region on a line of Text.
+ */
+public class TextFragmentBox
+    : ContentBox
+{
+
+/**
+ * The fragment's length in characters.
+ */
+public int length;
+
+/**
+ * The character offset at which this fragment begins.
+ */
+public int offset;
+
+private TextFlow textflow;
+private bool truncated;
+
+/**
+ * Creates a new TextFragmentBox for the given text flow.
+ * @param textflow the text flow
+ */
+public this(TextFlow textflow) {
+    this.textflow = textflow;
+}
+
+/**
+ * @see dwtx.draw2d.text.FlowBox#containsPoint(int, int)
+ */
+public bool containsPoint(int x, int y) {
+    return x >= getX()
+        && x < getX() + getWidth()
+        && y >= getBaseline() - getAscentWithBorder()
+        && y <= getBaseline() + getDescentWithBorder();
+}
+
+/**
+ * Returns the textflow's font's ascent. The ascent is the same for all fragments in a
+ * given TextFlow.
+ * @return the ascent
+ */
+public int getAscent() {
+    return textflow.getAscent();
+}
+
+int getAscentWithBorder() {
+    return textflow.getAscent() + FlowUtilities.getBorderAscent(textflow);
+}
+
+/**
+ * Returns the textflow's font's descent. The descent is the same for all fragments in a
+ * given TextFlow.
+ * @return the descent
+ */
+public int getDescent() {
+    return textflow.getDescent();
+}
+
+int getDescentWithBorder() {
+    return textflow.getDescent() + FlowUtilities.getBorderDescent(textflow);
+}
+
+int getOuterAscent() {
+    return textflow.getAscent() + FlowUtilities.getBorderAscentWithMargin(textflow);
+}
+
+int getOuterDescent() {
+    return textflow.getDescent() + FlowUtilities.getBorderDescentWithMargin(textflow);
+}
+
+final int getTextTop() {
+    return getBaseline() - getAscent();
+}
+
+/**
+ * Returns <code>true</code> if the bidi level is odd.  Right to left fragments should be
+ * queried and rendered with the RLO control character inserted in front.
+ * @return <code>true</code> if right-to-left
+ * @since 3.1
+ */
+public bool isRightToLeft() {
+    // -1 % 2 is -1
+    return getBidiLevel() % 2 is 1;
+}
+
+/**
+ * Returns <code>true</code> if the fragment should be rendered as truncated.
+ * @return <code>true</code> if the fragment is truncated
+ * @since 3.1
+ */
+public bool isTruncated() {
+    return truncated;
+}
+
+/**
+ * Marks the fragment as having been truncated.
+ * @param value <code>true</code> if the fragment is truncated
+ * @since 3.1
+ */
+public void setTruncated(bool value) {
+    this.truncated = value;
+}
+
+/**
+ * @see java.lang.Object#toString()
+ */
+public String toString() {
+    return Format("[{}, {}) = \"{}\"",offset, (offset + length), //$NON-NLS-1$ //$NON-NLS-2$
+        textflow.getText().substring(offset, offset + length) ); //$NON-NLS-1$
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/text/TextLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.text.TextLayout;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.text.FlowFigureLayout;
+import dwtx.draw2d.text.TextFlow;
+import dwtx.draw2d.text.TextFragmentBox;
+
+/**
+ * @author hudsonr
+ * @since 2.1
+ */
+public abstract class TextLayout : FlowFigureLayout {
+
+/**
+ * Creates a new TextLayout with the given TextFlow
+ * @param flow The TextFlow
+ */
+public this(TextFlow flow) {
+    super(flow);
+}
+
+/**
+ * Reuses an existing <code>TextFragmentBox</code>, or creates a new one.
+ * @param i the index
+ * @param fragments the original list of fragments
+ * @return a TextFragmentBox
+ */
+protected TextFragmentBox getFragment(int i, List fragments) {
+    if (fragments.size() > i)
+        return cast(TextFragmentBox)fragments.get(i);
+    TextFragmentBox box = new TextFragmentBox(cast(TextFlow)getFlowFigure());
+    fragments.add(box);
+    return box;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/widgets/ImageBorder.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.widgets.ImageBorder;
+
+import dwt.dwthelper.utils;
+
+
+
+import dwt.graphics.Image;
+import dwtx.draw2d.AbstractBorder;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Insets;
+import dwtx.draw2d.geometry.Rectangle;
+
+/**
+ * @author Pratik Shah
+ */
+class ImageBorder
+    : AbstractBorder
+{
+
+/*
+ * @TODO:Pratik Need to test this class extensively
+ * @TODO Test inside compound borders
+ */
+
+private Insets imgInsets;
+private Image image;
+private Dimension imageSize;
+
+public this(Image image) {
+    setImage(image);
+}
+
+public Insets getInsets(IFigure figure) {
+    return imgInsets;
+}
+
+public Image getImage() {
+    return image;
+}
+
+/**
+ * @see dwtx.draw2d.AbstractBorder#getPreferredSize(dwtx.draw2d.IFigure)
+ */
+public Dimension getPreferredSize(IFigure f) {
+    return imageSize;
+}
+
+public void paint(IFigure figure, Graphics graphics, Insets insets) {
+    if (image is null)
+        return;
+    Rectangle rect = getPaintRectangle(figure, insets);
+    int x = rect.x;
+    int y = rect.y + (rect.height - imageSize.height) / 2;
+    graphics.drawImage(getImage(), x, y);
+}
+
+public void setImage(Image img) {
+    image = img;
+    imageSize = new Dimension(image);
+    imgInsets = new Insets();
+    imgInsets.left = imageSize.width;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/widgets/MultiLineLabel.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,200 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.widgets.MultiLineLabel;
+
+import dwt.dwthelper.utils;
+
+
+
+import dwt.DWT;
+import dwt.accessibility.ACC;
+import dwt.accessibility.AccessibleAdapter;
+import dwt.accessibility.AccessibleControlAdapter;
+import dwt.accessibility.AccessibleControlEvent;
+import dwt.accessibility.AccessibleEvent;
+import dwt.events.KeyAdapter;
+import dwt.events.KeyEvent;
+import dwt.graphics.Font;
+import dwt.graphics.Image;
+import dwt.widgets.Composite;
+import dwtx.draw2d.Border;
+import dwtx.draw2d.ColorConstants;
+import dwtx.draw2d.FigureCanvas;
+import dwtx.draw2d.FocusEvent;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.MarginBorder;
+import dwtx.draw2d.Viewport;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.text.FlowPage;
+import dwtx.draw2d.text.TextFlow;
+import dwtx.draw2d.widgets.ImageBorder;
+
+/**
+ * A widget for displaying a multi-line string. The label will have a vertical or
+ * horizontal scrollbar when needed. Unlike the platform Label, this label is focusable
+ * and accessible to screen-readers.
+ *
+ * @author hudsonr
+ */
+public final class MultiLineLabel : FigureCanvas {
+
+private TextFlow textFlow;
+static const Border MARGIN;
+private Image image;
+
+static this(){
+    MARGIN = new MarginBorder(2);
+}
+
+class FocusableViewport : Viewport {
+    this() {
+        super(true);
+        setFocusTraversable(true);
+        setBorder(MARGIN);
+    }
+
+    public void handleFocusGained(FocusEvent event) {
+        super.handleFocusGained(event);
+        repaint();
+    }
+
+    public void handleFocusLost(FocusEvent event) {
+        super.handleFocusLost(event);
+        repaint();
+    }
+
+    protected void paintBorder(Graphics graphics) {
+        super.paintBorder(graphics);
+        if (hasFocus()) {
+            graphics.setForegroundColor(ColorConstants.black);
+            graphics.setBackgroundColor(ColorConstants.white);
+            graphics.drawFocus(getBounds().getResized(-1, -1));
+        }
+    }
+}
+
+/**
+ * Constructs a new MultiLineLabel with the given parent.
+ * @param parent the parent
+ */
+public this(Composite parent) {
+    super(parent);
+    setViewport(new FocusableViewport());
+
+    FlowPage page = new FlowPage();
+    textFlow = new TextFlow();
+    page.add(textFlow);
+
+    setContents(page);
+    getViewport().setContentsTracksWidth(true);
+    addAccessibility();
+}
+
+private void addAccessibility() {
+    getAccessible().addAccessibleControlListener(new class() AccessibleControlAdapter {
+        public void getRole(AccessibleControlEvent e) {
+            e.detail = ACC.ROLE_LABEL;
+        }
+        public void getState(AccessibleControlEvent e) {
+            e.detail = ACC.STATE_READONLY;
+        }
+    });
+    getAccessible().addAccessibleListener(new class() AccessibleAdapter {
+        public void getName(AccessibleEvent e) {
+            e.result = getText();
+        }
+    });
+    addKeyListener(new class() KeyAdapter {
+        public void keyPressed(KeyEvent e) {
+            Point p = getViewport().getViewLocation();
+            int dy = getFont().getFontData()[0].getHeight();
+            int dx = dy * 3 / 2;
+            bool mirrored = (e.widget.getStyle() & DWT.MIRRORED) !is 0;
+            if (e.keyCode is DWT.ARROW_DOWN) {
+                scrollToY(p.y + dy / 2);
+                scrollToY(p.y + dy);
+                scrollToY(p.y + dy * 3 / 2);
+                scrollToY(p.y + dy * 2);
+            } else if (e.keyCode is DWT.ARROW_UP) {
+                scrollToY(p.y - dy / 2);
+                scrollToY(p.y - dy);
+                scrollToY(p.y - dy * 3 / 2);
+                scrollToY(p.y - dy * 2);
+            } else if ((!mirrored && e.keyCode is DWT.ARROW_RIGHT)
+                    || (mirrored && e.keyCode is DWT.ARROW_LEFT)) {
+                scrollToX(p.x + dx);
+                scrollToX(p.x + dx * 2);
+                scrollToX(p.x + dx * 3);
+            } else if ((!mirrored && e.keyCode is DWT.ARROW_LEFT)
+                    || (mirrored && e.keyCode is DWT.ARROW_RIGHT)) {
+                scrollToX(p.x - dx);
+                scrollToX(p.x - dx * 2);
+                scrollToX(p.x - dx * 3);
+            }
+        }
+    });
+}
+
+/**
+ * @see dwt.widgets.Control#setEnabled(bool)
+ */
+public void setEnabled(bool enabled) {
+    super.setEnabled(enabled);
+    textFlow.setEnabled(getEnabled());
+}
+
+/**
+ * @return the Image for this label, or <code>null</code> if there is none
+ * @see #setImage(Image)
+ */
+public Image getImage() {
+    return image;
+}
+
+/**
+ * Returns the text in this label.
+ * @return the text
+ */
+public String getText() {
+    return textFlow.getText();
+}
+
+/**
+ * @see dwt.widgets.Canvas#setFont(dwt.graphics.Font)
+ */
+public void setFont(Font font) {
+    super.setFont(font);
+    textFlow.revalidate();
+}
+
+/**
+ * @param   image   The <code>Image</code> to be used for this label.  It can be
+ *                  <code>null</code>.
+ */
+public void setImage(Image image) {
+    this.image = image;
+    if (image !is null)
+        setBorder(new ImageBorder(image));
+    else
+        setBorder(null);
+}
+
+/**
+ * Sets the text for this label.
+ * @param text the new text
+ */
+public void setText(String text) {
+    textFlow.setText(text);
+}
+
+}
--- a/dwtx/dwtxhelper/mangoicu/UBreakIterator.d	Sat Jul 26 14:39:08 2008 +0200
+++ b/dwtx/dwtxhelper/mangoicu/UBreakIterator.d	Sun Aug 03 00:52:14 2008 +0200
@@ -366,25 +366,29 @@
 
         ***********************************************************************/
 
-        static UBreakIterator openWordIterator( ULocale locale, char[] str ){
+        static UBreakIterator openWordIterator( ULocale locale, char[] str = null ){
             UBreakIterator res;
-            res.ut.openUTF8(str);
             auto e = ICU.UErrorCode.OK;
             res.handle = ubrk_open( Type.Word, locale.name.ptr, null, 0, e);
             ICU.testError (e, "failed to open word iterator");
-            ubrk_setUText( res.handle, & res.ut, e);
-            ICU.testError (e, "failed to set text in iterator");
+            if( str ) {
+                res.ut.openUTF8(str);
+                ubrk_setUText( res.handle, & res.ut, e);
+                ICU.testError (e, "failed to set text in iterator");
+            }
             return res;
         }
 
-        static UBreakIterator openLineIterator( ULocale locale, char[] str ){
+        static UBreakIterator openLineIterator( ULocale locale, char[] str = null ){
             UBreakIterator res;
-            res.ut.openUTF8(str);
             auto e = ICU.UErrorCode.OK;
             res.handle = ubrk_open( Type.Line, locale.name.ptr, null, 0, e);
             ICU.testError (e, "failed to open line iterator");
-            ubrk_setUText( res.handle, & res.ut, e);
-            ICU.testError (e, "failed to set text in iterator");
+            if( str ) {
+                res.ut.openUTF8(str);
+                ubrk_setUText( res.handle, & res.ut, e);
+                ICU.testError (e, "failed to set text in iterator");
+            }
             return res;
         }